快慢指针在链表中的应用


 链表的操作题有很多,最常见的莫过于,单链表的反转之类,这里仅对可以用快慢指针来解决的题目进行解析。
        1、判断单链表中是否存在有环,如果有的话,找出这个环的入口节点;
        2、取单链表的中间节点;
        3、判断两个单链表是否相交,如果相交,找到交点;
解析:
      1、判断单链表中是否存在有环,如果有的话,找出这个环的入口节点;
      判断单链表是否存在环,使用快慢指针是比较明智的做法,设置两个指针,一个 fast,每次走两步,一个slow 每次走一步,直到 fast == slow 或者 链表结束。当 fast == slow 的时候,即存在环。
      如何找到环的入口节点?这里需要一个推理证明:
      假设起点是head,换入口点是entry,相遇点是meet,链表总长度是length,相遇时慢指针走了step 步,相遇时,快指针已经在环里转了N圈了,圈长是round,那么根据快慢指针的原理应该可以推断出这样的等式:
                     2*step = step + N*round  ==> step = N * round
       再设定变量 head_entry = 链表起点到环入口点的距离,entry_meet 为 环入口点到相遇点的距离,meet_entry 为相遇点到环入口点的距离,那么应该有:
                    round = length - head_entry
                    step = head_entry + entry_meet
                    meet_entry = length - head_entry - entry_meet
             由于 
                    step = N * round
             推出
                    step = N * round 
                            = (N -1) * round + round 
                            = ( N - 1) * round + length - head_entry
                            = head_entry + entry_meet
             进一步,得出:
                    head_entry = ( N - 1) * round + ( length - head_entry - entry_meet )
                                      = ( N - 1 ) * round + meet_entry
             得出以下结论:head 到 entry 的距离,等于 整数倍圈长 + 相遇点到环入口点的距离
             则设置两个指针,一个从链表起点出发,一个从相遇点触发,根据推理出来的公式,在第一次相遇时,相遇点出发的指针一定在圈内转了N圈,且走过了从相遇点到环入口点的距离,正好落在环入口点上,所以:
              从链表起点出发的指针和从相遇点出发的指针,第一次相遇的点就是环的入口点。
      2、取单链表的中间节点
      这是一个不错的题目,最简单的实现方式就是遍历两次,一次计算链表长度,再一次找到中间结点。但是如果链表比较长的话,这样遍历两次是比较不划算的,这里可以用到快慢指针,实现一次遍历即可找到中间节点。
      原理很简单,fast 和 slow 两个指针,因为已确定这个单链表里没有环,让fast 每次走两步,slow 每次走一步,则当fast 走完的时候,slow 正好走到链表的中间位置。
      3、判断两个单链表是否相交,如果相交,找到交点;
      这个问题需要先明确是单链表,所以发生的相交情形只有两种,丁字相交 和 一字相交,十字相交是不可能的,因为只有一个next 指针。
      这里可以利用 第一题 中的探测环的方式。我们假设两个链表分别是 A 和 B,然后将 A  的收尾相接,就可以得到一个环,然后从B 链表开始,使用快慢指针探测是否有环存在,如果相交,则必然可以探测到环。
      探测到之后,如何确定交点呢?
      这个还是利用第一题中的探测环的入口点是一样的了,因为交点必然是 B中 的环入口点。

      同样 从B 的起点 和 fast 与 slow 指针的交点分别设置两个指针,每次都走一步,第一次相遇的点就是 交点

### 快慢指针算法在链表中的应用场景及实现 快慢指针是一种常见的双指针技巧,在处理链表问题时非常有效。它通过两个速度不同的指针遍历链表来解决问题,能够显著降低时间复杂度并优化空间需求。 #### 应用场景分析 1. **检测环形链表** 使用快慢指针可以判断单向链表是否存在环结构。如果存在环,则快指针最终会追上慢指针;如果没有环,则快指针会先到达链表末尾[^1]。 2. **寻找环的入口节点** 当确认链表有环后,可以通过调整其中一个指针重新指向头结点的方式找到环的具体入口位置[^2]。 3. **查找中间节点** 利用快慢指针可以在一次遍历中定位到链表的中间节点。当快指针移动两步而慢指针只前进一步时,慢指针所处的位置即为链表中心。 4. **获取倒数第 K 个节点** 设置快指针先行走 K 步之后再启动慢指针同步前进,这样当快指针抵达终点时,慢指针正好处于目标位置之前的一个节点上。 #### 实现代码示例 以下是几个典型例子及其对应的 C++ 或 Python 的解决方案: ##### 检测是否有环 ```cpp ListNode* hasCycle(ListNode* head) { if (!head || !head->next) return nullptr; ListNode *slow = head, *fast = head; while (fast && fast->next){ slow = slow->next; // 移动一步 fast = fast->next->next; // 移动两步 if(slow == fast){ // 如果相遇则说明有环 return true; } } return false; // 遍历结束无环返回false } ``` ##### 找到环的起始点 ```python def detectCycle(head): if not head or not head.next: return None slow, fast = head, head # 查看是否成环 while fast and fast.next: slow = slow.next # 慢指针每次向前一格 fast = fast.next.next # 快指针每次跳过两格 if slow == fast: # 发现有交集 break # 跳出循环准备找入环口 else: # 若未发现任何交叉情况退出while loop return None # 表明不存在闭环直接return none即可 # 将其中任意一个重置回起点再次同时出发直到两者再度交汇于入环处为止 slow = head # 让慢指针回到头部开始新一轮迭代过程 # 这次保持相同步伐大小逐步靠近直至碰面那一刻就是我们要找的那个特殊地址啦! while slow != fast: slow = slow.next # 均匀增量推进至相逢之地~ fast = fast.next return slow # 返回该共同坐标作为结果输出。 ``` ##### 获取链表中间节点 ```java public class MiddleNodeFinder { public static ListNode findMiddle(ListNode head) { if (head == null || head.next == null) { return head; } ListNode slow = head; ListNode fast = head; while(fast != null && fast.next != null){ slow = slow.next; // Move one step at a time. fast = fast.next.next; // Move two steps ahead. /* When 'fast' reaches the end of list, 'slow' will be pointing to middle node */ } return slow; } } ``` ##### 寻找倒数第K个节点 ```javascript function getKthFromEnd(head, k) { let fast = head, slow = head; for(let i=0;i<k;i++) { // Advance `fast` pointer by 'k' positions first. if(!fast) return null; fast = fast.next; } while(fast !== null){ // Then move both pointers until 'fast' hits the tail. fast = fast.next; slow = slow.next; } return slow; // Now 'slow' points exactly where we need it - k-th from last element ! } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值