快慢指针问题【C语言】

一.链表的中间结点

题目描述:

给定一个头结点为head的非空单链表,返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点

要求只能遍历一遍

解析:

定义一个快指针:fast,一个慢指针:slow

利用两个指针的速度差

  • 个数为奇数时:

  • 个数为偶数时:

代码实现:

struct ListNode 
{
     int val;
     struct ListNode *next;
};

struct ListNode* middleNode(struct ListNode* head)
{
     struct ListNode *fast,*slow;
     slow = fast = head;
     while(fast&&fast->next)
     {
         slow=slow->next;
         fast = fast->next->next;
     }
     return slow;
}

二、链表中倒数第k个结点

题目描述:

输入一个链表,输出该链表中倒数第k个结点。只能遍历链表一遍。

示例:

输入:1,{1,2,3,4,5}

返回值:{5}

解析:

快慢指针并不是说一定是一个速度快 ,一个速度慢,而是用来拉开差距。

有两种方法:

  • 当fast先走k步(令k=2),再同时走:

以此类推....倒数第二个如下:

代码实现:

struct ListNode 
{
     int val;
     struct ListNode *next;
 };

struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
    struct ListNode *fast,*slow;
    fast = slow = pListHead;
    //fast先走k步
    while(k--)
    {
        //链表可能没有k步长
        if(fast == NULL)
        {
            return NULL;
        }
        fast = fast->next;
    }
    //fast和slow一起移动
    while(fast)
    {
        slow = slow->next;
        fast = fast->next;
    }
    return slow;
}

三、环形链表(一)

题目描述:

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

来源:力扣(LeetCode)

链接:https://leetcode.cn/problems/linked-list-cycle

输入:head = [3,2,0,-4], pos = 1

输出:true

解释:链表中有一个环,其尾部连接到第二个节点。

解析:

本质为快慢指针的追击问题,如果能追上,则为一个环。

代码实现:

 struct ListNode {
      int val;
      struct ListNode *next;
  };
 
bool hasCycle(struct ListNode *head) {
   struct ListNode* slow = head,*fast = head;
   while(fast && fast->next)//链表有可能不带环
   {
       slow = slow->next;
       fast = fast->next->next;
       if(slow == fast)//带环相遇
       return true;
   } 
   return false;
}

【扩展问题】

为什么快指针每次走两步,慢指针走一步可以?

假设链表带环,两个指针最后都会进入环,快指针先进环,慢指针后进环。当慢指针刚

进环时,可能就和快指针相遇了,最差情况下两个指针之间的距离刚好就是环的长度。

此时,两个指针每移动一次,之间的距离就缩小一步,不会出现每次刚好是套圈的情

况,因此:在满指针走到一圈之前,快指针肯定是可以追上慢指针的,即相遇。

快指针一次走3步,走4步,...n步行吗?

总结:速度差为1一定能追上

四、环形链表(二)

题目描述:

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

来源:力扣(LeetCode)

链接:https://leetcode.cn/problems/linked-list-cycle-ii

输入:head = [3,2,0,-4], pos = 1

输出:返回索引为 1 的链表节点

解释:链表中有一个环,其尾部连接到第二个节点。

解析:

方法一:让一个指针从链表起始位置开始遍历链表,同时让一个指针从判环时相遇点的位置开始绕环运行,两个指针都是每次均走一步,最终肯定会在入口点的位置相遇

 struct ListNode 
{
      int val;
     struct ListNode *next;
 };
 
struct ListNode *detectCycle(struct ListNode *head)
{
    struct ListNode* slow = head,*fast = head;
    while(fast&&fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow==fast)
        {
            struct ListNode* meet = slow;
            while(head!=meet)
            {
                head = head->next;
                meet = meet->next;
            }
            return meet;
        }
    }
    return NULL;
}

方法二:利用输入两个链表,找出它们第一个公共结点,这个结点即为入口点

 struct ListNode 
{
      int val;
     struct ListNode *next;
 };

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) 
{
    struct ListNode* curA =headA,*curB = headB;
    int lenA = 0,lenB = 0;
    while(curA)
    {
        ++lenA;
        curA = curA->next;
    }
    while(curB)
    {
        ++lenB;
        curB = curB->next;
    }
    //尾节点不相等就不相交
    if(curA!=curB)
         return NULL;
    int gap = abs(lenA-lenB);
    struct ListNode * longList = headA,*shortList = headB;
    if(lenB>lenA)
    {
        longList = headB;
        shortList = headA;
    }
    //长先走差距步
    while(gap--)
    {
        longList = longList->next;
    }
    //同时走,找交点
    while(longList != shortList)
    {
        longList = longList->next;
        shortList = shortList->next;
    }
    return longList;
}

struct ListNode *detectCycle(struct ListNode *head)
{
    struct ListNode* slow = head,*fast = head;
    while(fast&&fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow==fast)
        {
            struct ListNode* meet = slow;
            struct ListNode* otherHead = meet->next;
            meet->next = NULL;
            return getIntersectionNode(head,otherHead);
        }
    }
    return NULL;
}

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值