引入

快慢指针经常用于链表(linked list)中环(Cycle)相关的问题。LeetCode中对应题目分别是:

  1. 141. Linked List Cycle 判断linked list中是否有环
  2. 142. Linked List Cycle II 找到环的起始节点(entry node)位置。

简介

  1. 快指针(fast pointer)和慢指针(slow pointer)都从链表的head出发。
  2. slow pointer每次移动一格,而快指针每次移动两格。
  3. 如果快慢指针能相遇,则证明链表中有环;否则如果走到头了依然没有相遇,则没有环。

快慢指针的具体代码(C++, Python, Java版本)可以参考这个链接

问题详解 —— 为什么如果有环,则快慢指针必定会相遇?

我们假设以下变量:

\(L_1\):起始节点(head node)到环起始节点(entry node)的距离。

\(C\): 环的长度。

假设我们的慢指针移动了\(x\)步,那么快指针就移动了\(2x\)步。

那么必定有$$(x-L_1)% C= (2x-L_1) % C $$$$(2x-x)% C = 0$$$$x%C=0$$

以上三个式子步步可逆,由于\(C\)是给定的fixed value,而\(x\)每步都在上升,因此必定有一个\(x=wC(w\in {N})\)使得他们相遇。并且有\(L_1 \le x\)所以必有\(x=\)⌈\(\frac{L_1}{C}\)⌉\(C\)为他们第一次相遇的地点。因此有\(x< L_1 + C\) where \(x=\)⌈\(\frac{L_1}{C}\)⌉\(C\)

这也就意味着他们相遇的地方一定是慢指针在环里的第一圈。

问题详解 —— 如何找到环的起始节点?

我们再增加一些变量:

\(L_2\): 环起始节点(entry node)到快慢指针相遇节点的距离。

\(k\): 慢指针和快指针相遇的时候,慢指针走了的距离。

注意到,因为快指针走了的距离总是慢指针走了的距离的两倍,因此\(2k\)是慢指针和快指针相遇的时候,快指针走了的距离。

由慢指针可以得出$$L_1+L_2=k$$由快指针可以得出,其中n是快指针已经走过的圈数$$L_1+nC+L_2 = 2k$$结合上述两个式子,我们可以得出$$nC=k$$

当慢指针在遇到了快指针之后,慢指针又马上移动了,那么慢指针需要移动\(p\)步后就可以让慢指针就回到了环起始节点(entry node)。与此同时,在快慢指针相遇之后,又有一个指针马上从原点出发,那么它需要经过\(q\) 步才能到达起始节点(entry node)而且与慢指针相遇。

当慢指针和这个新指针相遇的时候,有$$(p-L_1)%C=(q+L_2)%C$$

离散数学中的定理可知$$(p-q-L_1-L_2)%C=0$$$$(p-q-nC)%C=(p-q)%C=0$$

不妨取\(p=q\)即\(p-q=0\Rightarrow (p-q)\%C=0\)

因此当他们同时回到环起始节点(entry node)的时候,有慢指针第一次相遇后走过的距离\(p\)和新指针走的距离\(p=q\)。

值得注意的是\(L_2< C\),因此,他们第一相遇的时候必有\(q<C-L_2\),也就是说慢指针和新指针第一次相遇的时候,他们必定都在环起始节点(entry node)。

LeetCode 对应代码

判断linked list中是否有环(141. Linked List Cycle)

这个算法的时间复杂度是O(n)

# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None class Solution(object):
def hasCycle(self, head):
"""
:type head: ListNode
:rtype: bool
"""
if head is None:
return False
slow = head
fast = head
while(fast.next and fast.next.next):
slow = slow.next
fast = fast.next.next
if slow == fast:
return True
return False

找到环的起始节点(entry node)位置(142. Linked List Cycle II)

# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None class Solution(object):
def detectCycle(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
if head is None:
return None
slow, fast, new_node = head, head, head
while(fast.next and fast.next.next):
slow = slow.next
fast = fast.next.next
if slow == fast:
while slow != new_node:
new_node = new_node.next
slow = slow.next
return new_node
return None

【算法分析】如何理解快慢指针?判断linked list中是否有环、找到环的起始节点位置。以Leetcode 141. Linked List Cycle, 142. Linked List Cycle II 为例Python实现的更多相关文章

  1. 141. Linked List Cycle&142. Linked List Cycle II(剑指Offer-链表中环的入口节点)

    题目: 141.Given a linked list, determine if it has a cycle in it. 142.Given a linked list, return the ...

  2. Leetcode 141题 环形链表(Linked List Cycle) Java语言求解

    题目描述: 给定一个链表,判断链表中是否有环. 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始). 如果 pos 是 -1,则在该链表中没有环. Map ...

  3. linked-list-cycle (快慢指针判断是否有环)

    class Solution { public: bool hasCycle(ListNode *head) { if (head == NULL) return NULL; //空表 ListNod ...

  4. Linked List Cycle I&&II——快慢指针(II还没有完全理解)

    Linked List Cycle I Given a linked list, determine if it has a cycle in it. Follow up: Can you solve ...

  5. LeetCode 141. Linked List Cycle 判断链表是否有环 C++/Java

    Given a linked list, determine if it has a cycle in it. To represent a cycle in the given linked lis ...

  6. [leetcode]141. Linked List Cycle判断链表是否有环

    Given a linked list, determine if it has a cycle in it. Follow up:Can you solve it without using ext ...

  7. reorder-list——链表、快慢指针、逆转链表、链表合并

    Given a singly linked list L: L0→L1→…→Ln-1→Ln,reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→… You must do thi ...

  8. sort-list——链表、快慢指针找中间、归并排序

    Sort a linked list in O(n log n) time using constant space complexity. 链表,快慢指针找中点,归并排序. 注意判断条件fast-& ...

  9. 深入理解C指针之三:指针和函数

    原文:深入理解C指针之三:指针和函数 理解函数和指针的结合使用,需要理解程序栈.大部分现代的块结构语言,比如C,都用到了程序栈来支持函数的运行.调用函数时,会创建函数的栈帧并将其推到程序栈上.函数返回 ...

随机推荐

  1. 【微信开发】微信开发模式 api 接口文档简介

    微信公众平台分为订阅号和服务号,服务号提供9大接口,需要通过微信认证后才能使用这些接口.认证费用300元.下面是接口的大致介绍: 1. 语音识别:通过语音识别接口,用户发送的语音,将会同时给出语音识别 ...

  2. Oracle中按规定的字符截取字符串

    CREATE OR REPLACE FUNCTION "F_SPLIT" (p_str IN CLOB, p_delimiter IN VARCHAR2) RETURN ty_st ...

  3. iOS:文件操作相关(18-03-23更)

    0.iOS文件系统 1.工程内文件 2.文件夹管理 3.文件操作 4.NSCache 附录: 1.沙盒文件夹.文件大小 2.清除沙盒 Library / Cache 下所有数据 3.测试plist 0 ...

  4. 学习笔记 - 2sat

    学习笔记 - 2sat 决定重新启用Markdown--只是因为它支持MathJax数学公式 noip考完,既轻松又无奈,回来慢慢填坑 这篇博客也是拖了好久,通过kuangbin的博客才弄懂2-sat ...

  5. window下pip install Scrapy报错解决方案

    1.首先打开https://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted,找到对应版本的Twisted并下载到你的文件夹. 2.利用pip install命令 ...

  6. css:url链接去下划线+点击前黑色+点击时灰色+点击后黑色

    一般的文章列表 加了样式之后的效果 附上css代码 /*点击前*/ a:link{ color: black; } /*点击后*/ a:visited{ color: black; } /*点击时*/ ...

  7. go加密算法:非对称加密(一)--RSA

    椭圆曲线加密__http://blog.51cto.com/11821908/2057726 // MyRas.go package main import ( "crypto/rand&q ...

  8. vuejs 预渲染插件 prerender-spa-plugin 生成多页面 -- SEO

    前端vue等框架打包的项目一般为SPA应用,而单页面是不利于SEO的,现在的解决方案有两种: 1.SSR服务器渲染   了解服务器渲染请进,这里不做记录. 2.预渲染模式   这比服务端渲染要简单很多 ...

  9. Spring security学习笔记(二)

    对比两种承载认证信息的方式: session vs token token验证方案: session验证方案: session即会话是将用户信息保存在服务端,根据请求携带的session_id,从服务 ...

  10. Altium Designer常用快捷键

    一:Altium原理图快捷键:    Shift+左键选择    :实现多个目标选择    Ctrl+左键拖动    :保持连线拖动目标        Shift+c       :清除当前过滤(?? ...