道阻且长,行则将至,走|

Ac_c0mpany丶

园龄:3年7个月粉丝:6关注:3

📂算法
🔖算法
2021-07-25 12:07阅读: 164评论: 0推荐: 0

双指针技巧之快慢指针

双指针技巧分为两类:快慢指针左右指针,前者主要解决链表中的问题,后者用来解决数组和字符串问题,下面将详细介绍快慢指针.

快慢指针的常用算法

快慢指针初始化时一般指向链表的头结点head,快指针fast在前,一次走两步,慢指针slow在后,一次走一步。

快慢指针典例1:判断链表中是否有环

我们很容易会想到一个方法:通过判断指针的下一结点是否为空,来判断链表中是否含环

bool hasCycle(LinkList* head){
  Linklist* p = head;
  while(p){
    p = p->next;
  }
  return false; //如果p为空,则链表有环
}

但是,如果链表中有环时,while会陷入死循环,因为环形链表中没有NULL指针作为尾部结点,所以单指针的方法是行不通的,这就需要我们用双指针中的快慢指针来解决。
快慢指针中,快指针一次走两步,慢指针一次走一步。如果链表不含环,快指针最终会指向NULL,说明链表不含环;如果含有环,快指针最终会追上慢指针,两者相遇,说明链表含环。

bool hasCycle(ListNode* head){
  ListNode* fast, * slow; //定义快、慢指针
  fast = slow = head; //初始化时快、慢指针指向头结点
  while(fast && fast->next){ //循环条件:fast不为NULL 且 fast->next不为NULL(主要是为了下句代码fast->next->next不出现空指针异常)
    fast = fast->next->next; //快指针一次走两步
    slow = slow->next; //慢指针一次走一步
    if(fast == slow) return true; //快指针追上慢指针,两者相遇,说明链表含环。
  }
  return false; //快指针指向```NULL```或者快指针的下一结点为```NULL```,说明链表不含环
}

LeetCode 141. 环形链表


/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
        ListNode* fast, * slow;
        fast = slow = head;
        while(fast && fast->next){
            fast = fast->next->next;
            slow = slow->next;
            if(fast == slow) return true;//若快慢指针相遇,则表示链表中有环
        }
        return false;//快指针为空或者快指针的下一结点指向空,则表示链表中没有环
    }
};

快慢指针典例1进阶:已知链表中含有环,返回这个环的起始位置

ListNode *detectCycle(ListNode *head){
    ListNode * fast, * slow;
    fast = slow = head;    
    while(fast && fast->next){
        fast = fast->next->next;     
        slow = slow->next;
        if(fast == slow) break; //快慢指针相遇
    }
    if(!fast || !(fast->next)) return NULL;
    slow = head; //让快指针或者慢指针指向head
    while(slow != fast){
        fast = fast->next; //两个指针以同样的速度向前
        slow = slow->next;
    }
    return slow; //当再次相遇时,slow或fast的位置就是环的入口
}

LeetCode 142. 环形链表 II

类似:ACwing 34.链表中环的入口结点

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
    public:
    ListNode *detectCycle(ListNode *head){
        ListNode * fast, * slow;
        fast = slow = head;    
        while(fast && fast->next){
            fast = fast->next->next;     
            slow = slow->next;
            if(fast == slow) break; //快慢指针相遇
        }
        if(!fast || !(fast->next)) return NULL;
        slow = head; //让快指针或者慢指针指向head
        while(slow != fast){
            fast = fast->next; //两个指针以同样的速度向前
            slow = slow->next;
        }
        return slow; //当再次相遇时,slow或fast的位置就是环的入口
    }
};

快慢指针典例2:寻找无环单链表的中点

我们很容易想到一个方法:先遍历一次链表统计出链表的结点数n,然后再遍历一次链表找到第n/2个结点,但是这种做法不是很高效,下面介绍快慢指针的方法。
快指针一次走两步,慢指针一次走一步,当快指针到达链表尽头时,慢指针的位置就是中间结点的位置

ListNode* middleNode(ListNode* head){
  ListNode* fast, * slow;
  fast = slow = head;
  while(fast && fast->next){
    fast = fast->next->next;
    slow = slow->next;
  }
  return slow; //快指针到达尽头时,慢指针的位置就是中间结点的位置
}

LeetCode 876. 链表的中间结点

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* middleNode(ListNode* head) {
        ListNode* fast, * slow;
        fast = slow = head;
        while(fast && fast->next){
            fast = fast->next->next;
            slow = slow->next;
        }
        return slow;
    }
};

快慢指针典例2进阶:寻找单链表的倒是第k个元素


思路:先让快指针走k步,然后快慢指针同时以相同的速度前进,当快指针到链表末尾NULL时,慢指针所指的位置就是倒数第k个元素。

ListNode* getKthFromEnd(ListNode* head, int k){ //假设k不会超过链表长度 具体问题具体分析
  ListNode* fast, * slow;
  fast = slow = head;  
  while(k--){
    fast = fast->next; //快指针先走k步
  }
  while(fast){
    fast = fast->next; // 快慢指针同时以相同的速度前进
    slow = slow->next;
  }
  return slow; //最后慢指针所指的位置就是倒数第k个元素
}

剑指 Offer 22. 链表中倒数第k个节点

类似:面试题 02.02. 返回倒数第 k 个节点
类似:ACwing 33. 链表中倒数第k个节点


/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* getKthFromEnd(ListNode* head, int k) { //此题k不会超过链表长度
        ListNode* fast, * slow;
        fast = slow = head;
        while(k--){
            fast = fast->next;
        }
        while(fast){
            fast = fast->next; 
            slow = slow->next;
        }
        return slow;
    }
};

19. 删除链表的倒数第 N 个结点



小技巧:在对链表进行操作时,一种常用的技巧是添加一个哑节点(dummy node),它的next指针指向链表的头节点。这样一来,我们就不需要对头节点进行特殊的判断了。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) { //依题意链表中至少有一个结点, n是有效值
       ListNode* dummy = new ListNode(); // 定义一个哑结点dummy 避免对头结点进行特殊判断
       dummy->next = head;
       ListNode* fast, * slow, * p;
       fast = slow = dummy;
       int k = n + 1; //删除倒数第n个元素,则找到倒数第n+1个元素
       while(k--){ //快指针先走n+1步
           fast = fast->next;
       }
       while(fast){ //快慢指针一起走
            fast = fast->next;
            slow = slow->next;
       }
       //慢指针就停在倒数第n+1个结点
       p = slow->next;
       slow->next = p->next;
       delete p; //释放删除结点

       return dummy->next; //注意不是返回dummy
    }
};

本文作者:keyongkang

本文链接:https://www.cnblogs.com/keyongkang/p/15057287.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Ac_c0mpany丶  阅读(164)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 You Are My Sunshine REOL
You Are My Sunshine - REOL
00:00 / 00:00
An audio error has occurred.

作曲 : Traditional

You are my sunshine

My only sunshine.

You make me happy

When skies are gray.

You'll never know, dear,

How much I love you.

Please don't take my sunshine away

The other night, dear,

When I lay sleeping

I dreamed I held you in my arms.

When I awoke, dear,

I was mistaken

So I hung my head and cried.

You are my sunshine,

My only sunshine.

You make me happy

When skies are gray.

You'll never know, dear,

How much I love you.

Please don't take my sunshine away.

You are my sunshine,

My only sunshine

You make me happy

When skies are gray.

You'll never know, dear

How much I love you

Please don't take my sunshine away

Please don't take my sunshine away.

Please don't take my sunshine away.