快慢指针在链表中的应用

快慢指针也是面试中的一个常考知识点,主要是链表的问题中应用较多。

1. 判断链表是否存在环

设置两个指针(fast, slow),初始值都指向头,slow每次前进一步,fast每次前进二步,如果链表存在环,则fast必定先进入环,而slow后进入环,两个指针必定相遇。(当然,fast先行头到尾部为NULL,则为无环链表)程序如下:

01 int isExitsLoop(LinkList L) {
02LinkList fast, slow;
03fast = slow = L;
04 while (fast && fast->next)
05{
06slow = slow->next;
07fast = fast->next->next;
08 if (slow == fast)
09{
10 break;
11}
12}
13 return ((fast == NULL) || (fast->next == NULL));
14}

找到环的入口点

当fast若与slow相遇时,slow肯定没有走遍历完链表,而fast已经在环内循环了n圈(1<=n)。假设slow走了s步,则fast走了2s步(fast步数还等于s 加上在环上多转的n圈),设环长为r,则:

12s = s + nr
2s= nr

设整个链表长L,入口环与相遇点距离为x,起点到环入口点的距离为a。

1a + x = nr
2a + x = (n – 1)r +r = (n-1)r + L - a
3a = (n-1)r + (L – a – x)

(L – a – x)为相遇点到环入口点的距离,由此可知,从链表头到环入口点等于(n-1)循环内环+相遇点到环入口点,于是我们从链表头、与相遇点分别设一个指针,每次各走一步,两个指针必定相遇,且相遇第一点为环入口点。程序描述如下:

01node* findLoopPort(node *head) {
02node *fast, *slow;
03fast = slow = head;
04 while (fast && fast->next) {
05slow = slow->next;
06fast = fast->next->next;
07 if (slow == fast) {
08 break;
09}
10}
11 if ((fast == NULL) || (fast->next == NULL)) {
12 return NULL;
13}
14slow = head;
15 while (slow != fast) {
16slow = slow->next;
17fast = fast->next;
18}
19 return slow;
20}

如何快速找出未知长度单链表的中间节点?

普通方法:先遍历一遍单链表确定其长度L后,再从头节点出发循环L/2次即可查找到单链表的中间节点。该问题如果采用普通的方法虽然简单,但是查找效率太低。

快慢指针: 设置两个指针*fast、*slow都指向单链表的头节点,其中*fast的移动速度是*slow的2倍,当*fast指向末尾节点的时候,slow正好就在中间了,可以大大提高查找的效率。

当然,此时算法 还要考虑链表结点个数的奇偶数因素,当快指针移动x次后到达表尾(1+2x),说明链表有奇数个结点,直接返回慢指针指向的数据即可。如果快指针是倒数第二个结点,说明链表结点个数是偶数,这时 可以 实际情况 返回上中位数或下中位数或(上中位数+下中位数)的一半。

这个应用实际在前面讲到的对链表进行排序有用过。

参考 :http://www.acmerblog.com/fast-slow-pointer-5382.html

### 快慢指针算法链表中的应用场景及实现 快慢指针是一种常见的双指针技巧,在处理链表问题时非常有效。它通过两个速度不同的指针遍历链表来解决问题,能够显著降低时间复杂度并优化空间需求。 #### 应用场景分析 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、付费专栏及课程。

余额充值