“双指针”法解决链表问题
2018-04-02 22:14
309 查看
背景:单链表问题由于顺序遍历的特性,有时候执行一些操作的时候会出现问题看似需要多次遍历才能获取数据。使用双指针法能在一次遍历中获取更多的数据,也可以节约更多的额外控件。
“双指针”就是用一个快指针一个慢指针同时进行单链表的顺序扫描。如此就可以使用快指针的时间差给慢指针提供更多的操作信息。下面是两个LeetCode下的习题。
(1)给定一个链表,删除链表的倒数第 n 个节点并返回头结点。
例如,给定一个链表: 1->2->3->4->5, 并且 n = 2.
当删除了倒数第二个节点后链表变成了 1->2->3->5.
思路:构建先导指针,快于后续指针n-1步,先导指针指向链表尾部时候,慢指针就指向倒数第n个节点 ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* p = head;
ListNode* pe = head;
ListNode* pl = NULL;
for (int i = 0; i < n - 1; ++i){
pe = pe->next;
}
if (pe->next == NULL){
return head->next;
}
pe = pe->next;
p = p->next;
pl = head;
while(pe->next != NULL){
pe = pe->next;
p = p->next;
pl = pl->next;
}
if (p->next){
pl->next = p->next;
}
else{
pl->next = NULL;
}
return head;
}这个答案可以用“dummy node”来优化:增加n-1个先导节点进行优化,这样就不用进行比较繁杂的判空操作,直接告诉向下遍历即可。
(2)给定一个链表,判断链表中否有环。
传统思路:找个容器把出现的点存起来,出现重复点就判断成环(缺点很明显:额外内存占用)bool hasCycle(ListNode *head) {
std::set<ListNode*> sl;
if (!head){
return false;
}
while(head->next){
if (sl.find(head) != sl.end()){
return true;
}
else{
sl.insert(head);
head = head->next;
}
}
return false;
}双指针思路:快指针比慢指针先走,如果快指针被慢指针追上,说明进入闭环ListNode* fastNode = head;
ListNode* slowNode = head;
if (head == nullptr)
{
return false;
}
while (fastNode && fastNode->next)
{
slowNode = slowNode->next;
fastNode = fastNode->next->next;
if (slowNode == fastNode)
{
return true;
}
}总结:用双指针能够靠速度差或者位置差更加方便的处理定位和列表结构的信息,从而构造出更低复杂度的算法。
“双指针”就是用一个快指针一个慢指针同时进行单链表的顺序扫描。如此就可以使用快指针的时间差给慢指针提供更多的操作信息。下面是两个LeetCode下的习题。
(1)给定一个链表,删除链表的倒数第 n 个节点并返回头结点。
例如,给定一个链表: 1->2->3->4->5, 并且 n = 2.
当删除了倒数第二个节点后链表变成了 1->2->3->5.
思路:构建先导指针,快于后续指针n-1步,先导指针指向链表尾部时候,慢指针就指向倒数第n个节点 ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* p = head;
ListNode* pe = head;
ListNode* pl = NULL;
for (int i = 0; i < n - 1; ++i){
pe = pe->next;
}
if (pe->next == NULL){
return head->next;
}
pe = pe->next;
p = p->next;
pl = head;
while(pe->next != NULL){
pe = pe->next;
p = p->next;
pl = pl->next;
}
if (p->next){
pl->next = p->next;
}
else{
pl->next = NULL;
}
return head;
}这个答案可以用“dummy node”来优化:增加n-1个先导节点进行优化,这样就不用进行比较繁杂的判空操作,直接告诉向下遍历即可。
(2)给定一个链表,判断链表中否有环。
传统思路:找个容器把出现的点存起来,出现重复点就判断成环(缺点很明显:额外内存占用)bool hasCycle(ListNode *head) {
std::set<ListNode*> sl;
if (!head){
return false;
}
while(head->next){
if (sl.find(head) != sl.end()){
return true;
}
else{
sl.insert(head);
head = head->next;
}
}
return false;
}双指针思路:快指针比慢指针先走,如果快指针被慢指针追上,说明进入闭环ListNode* fastNode = head;
ListNode* slowNode = head;
if (head == nullptr)
{
return false;
}
while (fastNode && fastNode->next)
{
slowNode = slowNode->next;
fastNode = fastNode->next->next;
if (slowNode == fastNode)
{
return true;
}
}总结:用双指针能够靠速度差或者位置差更加方便的处理定位和列表结构的信息,从而构造出更低复杂度的算法。
相关文章推荐
- 《LeetBook》leetcode题解(19):Remove Nth Node From End of List[E]——双指针解决链表倒数问题
- 快慢指针解决单向链表是否有环的一系列问题
- 算法学习笔记之四:巧妙运用指针解决链表、字符串、数组等问题(同向双指针VS对向双指针)
- 【链表】C++快慢两个指针解决问题(分别求倒数第k个、中间以及环的入口结点)
- 使用指针链表解决约瑟夫环问题
- web连接数据库时,报空指针java.lang.NullPointerException问题--可能的解决方式
- 真正解决FC5、FC6看不到鼠标指针(光标)的问题
- 环形链表解决约瑟夫游戏问题
- 解决Ubuntu安装VMware tools后鼠标指针平滑移动问题
- 链表解决约瑟夫问题
- C++不带头结点的单循环链表解决约瑟夫环问题
- 解决iOS空指针数据的问题
- 从链表操作理解C++传指针的问题
- 用单向循环链表解决约瑟夫环(Joseph)问题
- 约瑟夫问题链表解决方法(带有析构函数)
- 学习_指针操作解决约瑟夫出圈问题
- 智能指针weak_ptr解决循环依赖问题
- 循环链表解决约瑟夫环问题
- 解决this.getHibernateTemplate().save();报空指针异常问题
- Android Studio使用Butterknife时出现空指针问题解决