117

推荐

编辑于 2015-08-25 11:27:14

回复(43)

287

思路:

设置快慢指针,都从链表头出发,快指针每次走两步,慢指针一次走一步,假如有环,一定相遇于环中某点(结论1)。接着让两个指针分别从相遇点和链表头出发,两者都改为每次走一步,最终相遇于环入口(结论2)。以下是两个结论证明:

两个结论:

1、设置快慢指针,假如有环,他们最后一定相遇。

2、两个指针分别从链表头和相遇点继续出发,每次走一步,最后一定相遇与环入口。

证明结论1:设置快慢指针fast和low,fast每次走两步,low每次走一步。假如有环,两者一定会相遇(因为low一旦进环,可看作fast在后面追赶low的过程,每次两者都接近一步,最后一定能追上)。

证明结论2:

设:

链表头到环入口长度为--a

环入口到相遇点长度为--b

相遇点到环入口长度为--c

则:相遇时

快指针路程=a+(b+c)k+b ,k>=1  其中b+c为环的长度,k为绕环的圈数(k>=1,即最少一圈,不能是0圈,不然和慢指针走的一样长,矛盾)。

慢指针路程=a+b

快指针走的路程是慢指针的两倍,所以:

(a+b)*2=a+(b+c)k+b

化简可得:

a=(k-1)(b+c)+c 这个式子的意思是: 链表头到环入口的距离=相遇点到环入口的距离+(k-1)圈环长度。其中k>=1,所以k-1>=0圈。所以两个指针分别从链表头和相遇点出发,最后一定相遇于环入口。 C++版:

class Solution {

public:

ListNode* EntryNodeOfLoop(ListNode* pHead)

{

ListNode*fast=pHead,*low=pHead;

while(fast&&fast->next){

fast=fast->next->next;

low=low->next;

if(fast==low)

break;

}

if(!fast||!fast->next)return NULL;

low=pHead;//low从链表头出发

while(fast!=low){//fast从相遇点出发

fast=fast->next;

low=low->next;

}

return low;

}

}; java版:

public class Solution {

public ListNode EntryNodeOfLoop(ListNode pHead)

{

ListNode fast=pHead;

ListNode low=pHead;

while(fast!=null&&fast.next!=null){

fast=fast.next.next;

low=low.next;

if(fast==low)

break;

}

if(fast==null||fast.next==null)

return null;

low=pHead;

while(fast!=low){

fast=fast.next;

low=low.next;

}

return low;

}

}

编辑于 2019-12-04 12:29:55

回复(46)

336

【转】

http://kekecv.com/2016/06/08/Linked-List-Cycle-%E5%88%A4%E6%96%AD%E9%93%BE%E8%A1%A8%E6%98%AF%E5%90%A6%E6%9C%89%E7%8E%AF%EF%BC%8C%E5%A6%82%E6%9E%9C%E6%9C%89%E7%8E%AF%EF%BC%8C%E6%89%BE%E5%88%B0%E7%8E%AF%E7%9A%84%E5%85%A5%E5%8F%A3/

假设x为环前面的路程(黑色路程),a为环入口到相遇点的路程(蓝色路程,假设顺时针走), c为环的长度(蓝色+橙色路程)

当快慢指针相遇的时候:

此时慢指针走的路程为Sslow =

x + m * c + a

快指针走的路程为Sfast = x + n * c + a

2 Sslow =

Sfast

2 * ( x + m*c + a ) = (x + n *c + a)

从而可以推导出:

x = (n

- 2 * m )*c - a

= (n - 2 *m -1 )*c + c - a

即环前面的路程 =

数个环的长度(为可能为0) + c - a

什么是c - a?这是相遇点后,环后面部分的路程。(橙色路程)

所以,我们可以让一个指针从起点A开始走,让一个指针从相遇点B开始继续往后走,

2个指针速度一样,那么,当从原点的指针走到环入口点的时候(此时刚好走了x)

从相遇点开始走的那个指针也一定刚好到达环入口点。

所以2者会相遇,且恰好相遇在环的入口点。

最后,判断是否有环,且找环的算法复杂度为:

时间复杂度:O(n)

空间复杂度:O(1)

下面的是断链法。不过题目要求不允许修改链表时就尴尬了。

编辑于 2017-03-11 10:47:33

回复(67)

379

第一步,找环中相汇点。分别用p1,p2指向链表头部,p1每次走一步,p2每次走二步,直到p1==p2找到在环中的相汇点。

第二步,找环的入口。接上步,当p1==p2时,p2所经过节点数为2x,p1所经过节点数为x,设环中有n个节点,p2比p1多走一圈有2x=n+x;

n=x;可以看出p1实际走了一个环的步数,再让p2指向链表头部,p1位置不变,p1,p2每次走一步直到p1==p2;

此时p1指向环的入口。

public class Solution {

ListNode EntryNodeOfLoop(ListNode pHead){

if(pHead == null || pHead.next == null)

return null;

ListNode p1 = pHead;

ListNode p2 = pHead;

while(p2 != null && p2.next != null ){

p1 = p1.next;

p2 = p2.next.next;

if(p1 == p2){

p2 = pHead;

while(p1 != p2){

p1 = p1.next;

p2 = p2.next;

}

if(p1 == p2)

return p1;

}

}

return null;

}

}

发表于 2015-09-15 15:23:04

回复(65)

65

HashSet set = new HashSet();

while (pHead != null) {

if (!set.add(pHead)) {

return pHead;

}

pHead = pHead.next;

}

returnnull;

发表于 2015-10-07 12:45:55

回复(34)

32

//左神讲的

//先说个定理:两个指针一个fast、一个slow同时从一个链表的头部出发

//fast一次走2步,slow一次走一步,如果该链表有环,两个指针必然在环内相遇

//此时只需要把其中的一个指针重新指向链表头部,另一个不变(还在环内),

//这次两个指针一次走一步,相遇的地方就是入口节点。

//这个定理可以自己去网上看看证明。

public class Solution {

public ListNode EntryNodeOfLoop(ListNode pHead){

ListNode fast = pHead;

ListNode slow = pHead;

while(fast != null && fast.next !=null){

fast = fast.next.next;

slow = slow.next;

if(fast == slow)

break;

}

if(fast == null || fast.next == null)

return null;

fast = pHead;

while(fast != slow){

fast = fast.next;

slow = slow.next;

}

return fast;

}

}

发表于 2017-08-17 11:29:22

回复(13)

114

/*

时间复杂度为O(n),两个指针,一个在前面,另一个紧邻着这个指针,在后面。

两个指针同时向前移动,每移动一次,前面的指针的next指向NULL。

也就是说:访问过的节点都断开,最后到达的那个节点一定是尾节点的下一个,

也就是循环的第一个。

这时候已经是第二次访问循环的第一节点了,第一次访问的时候我们已经让它指向了NULL,

所以到这结束。

*/

class Solution {

public:

ListNode* EntryNodeOfLoop(ListNode* pHead)

{

if (!pHead->next)

return NULL;

ListNode* previous = pHead;

ListNode* front = pHead ->next;

while (front)

{

previous->next = NULL;

previous = front;

front = front->next;

}

return previous;

}

};

编辑于 2016-01-26 16:36:01

回复(39)

15

class Solution {

public:

ListNode* EntryNodeOfLoop(ListNode* pHead)

{

set s;

ListNode* node = pHead;

while(node!=NULL){

if(s.insert(node).second)

node = node->next;

else

return node;

}

return node;

}

};

我这里用到了STL中的set,set有一个特性就是不能插入相同元素,这样只需遍历原List一次就可以判断出有没有环,还有环的入口地址。s.insert(node).second这里在插入的同时也判断了插入是否成功,如果不成功表明set中已经有该元素了,该元素就是环的入口元素。

发表于 2016-07-23 18:20:48

回复(7)

30

python solution: # -*- coding:utf-8 -*-

# class ListNode:

# def __init__(self, x):

# self.val = x

# self.next = None

class Solution:

def EntryNodeOfLoop(self, pHead):

# write code here

slow,fast=pHead,pHead

while fast and fast.next:

slow=slow.next

fast=fast.next.next

if slow==fast:

slow2=pHead

while slow!=slow2:

slow=slow.next

slow2=slow2.next

return slow

发表于 2017-10-07 19:25:27

回复(14)

24

思路:leetcode上也有这道题,具体思想是,两个指针fast和slow,fast以slow两倍速度前进,

如果没有环,那么fast和slow不会相遇此时返回null;如果有环,那fast和slow肯定会再次相遇

相遇的时候,fast刚好比slow多走了一圈环的长度。

用图来描述下,当fast与slow相遇时,fast走过的距离为a + b + c + b,而slow走过的距离为

a + b,因为fast是slow速度的两倍,则有a+b+c+b = 2*(a+b),登出a=c;此时slow节点所处X处

到环起点Y处的距离a和X节点到Y处距离c其实是相等的,此时第三个指针p从x处,以和slow指针

相同的速度前进,当它两相遇时,即为环的起点Y处!

public class Solution {

public ListNode EntryNodeOfLoop(ListNode pHead)

{

ListNode fast = pHead;

ListNode slow = pHead;

while(fast !=null && fast.next !=null) {

fast = fast.next.next;

slow = slow.next;

if(fast == slow) {

ListNode p = pHead;

while( p != slow) {

p = p.next;

slow = slow.next;

}

return p;

}

}

return null;

}

}

编辑于 2017-05-18 13:02:17

回复(9)

13

要寻找环的入口节点,遍历节点的时候,遇到的第一个重复节点肯定入环节点,所以定义一个Set,

添加失败时 即返回入口节点

import java.util.*;

public class Solution {

public ListNode EntryNodeOfLoop(ListNode pHead)

{

if(pHead==null)

return null;

ListNode pNode=pHead;

HashSet pSet = new HashSet();

while(pNode!=null){

if(!pSet.add(pNode))

return pNode;

pNode=pNode.next;

}

return null;

}

}

发表于 2016-06-18 10:34:08

回复(6)

8

class Solution:

def EntryNodeOfLoop(self, pHead):

# write code here

linkls = []

while pHead:

if pHead in linkls:

return pHead

linkls.append(pHead)

pHead = pHead.next

return None

发表于 2018-07-05 19:59:29

回复(13)

5

先贴代码,再解释; public ListNode EntryNodeOfLoop(ListNode pHead)

{

if (pHead == null || pHead.next == null)

return null;

//step1 发现环

ListNode walk = pHead, run = pHead;

do {

walk = walk.next;

run = run.next.next;

} while (walk != run);

//step2 找到了环,并且run正在环内

//另起一个quickWalk从pHead开始走,quickWalk和run再次相遇的地点就是环的入口

ListNode quickWalk = pHead;

while (quickWalk != run) {

run = run.next.next;

quickWalk = quickWalk.next.next;

}

return quickWalk;

}

}第一步发现环,并找到了walk指针和run指针相遇的地点

第二步另起一个quickWalk指针,和run指针保持同步,俩指针相遇点即为入口;

解释:

如图,每个距离的含义: S:环入口到walk和run相遇点的距离

L:walk和run相遇点到入口的距离

P:头结点到环入口的距离

设walk的速度是v,则run的速度是2v;

设相遇花费了t时间,walk走过的路程为Swalk

根据题意有: 2vt - vt = vt,

又相遇的时候:vt = k(S+L)--①成立;

且 Swalk = vt = P + m(S+L) + S--②成立;

由①②可得 k(S+L) = P + m(S+L) + S

变形为:P = (k-m-1)(S+L) + L。

设 q = (k-m-1),最终得到 P = q(S+L) + L。

因此分别从相遇地点、头结点同时以相同的速度走的俩指针,最终相遇地点一定是环的入口。

PS:最后两个同步指针的速度应该是越快越好,因此,采用另起一个quickWalk指针和run同步,而不是和walk同步。

编辑于 2017-12-18 15:45:35

回复(1)

4

public class Solution {

public ListNode EntryNodeOfLoop(ListNode pHead) {

if(pHead == null || pHead.next == null) return null;

ListNode slow = pHead;

ListNode fast = pHead;

while (fast != null) {

fast = fast.next.next;

slow = slow.next;

if(fast == slow) break;

}

fast = pHead;

while (fast != slow) {

fast = fast.next;

slow = slow.next;

}

return slow;

}

}

发表于 2017-04-26 15:38:04

回复(0)

6

'''

解法:

第一步,先找到环中的一个节点(找到之后就可以计算出环中的节点数目)

让一个指针fast走快一点,一个指针slow走慢一点;当fast与slow相遇时,即fast比slow多一圈,此时相遇的节点肯定是环内的节点

第二步,根据环中的节点来计算环中节点总数

从此节点遍历到此节点结束,即可得到节点总数

第三步,知道了环内节点总数,来找到环入口

先让一个指针p1从根节点开始往后走m步,然后再让一个节点p2指向头结点;

然后让p1和p2同时往后移动,当p1与p2相交时,此时的点就是环的入口节点

'''

class Solution:

def EntryNodeOfLoop(self, pHead):

# write code here

meet_node = self.MeetNode(pHead)

if meet_node == None:

return None

# 得到环中的节点个数

loop_nodes = 1

p1 = meet_node

while p1.next != meet_node:

loop_nodes += 1

p1 = p1.next

# 目前已经得到了环中节点个数m,和环中个一个节点meetnode,如何找到环的入口?

# 一个指针p1从根节点开始往后走m步,然后再让一个节点p2指向头结点,p1和p2同时往后移动,当p1与p2相交时,此时的点就是环的入口节点

p1 = pHead

for i in range(loop_nodes):

p1 = p1.next

p2 = pHead

while p1 != p2:

p1 = p1.next

p2 = p2.next

return p1

def MeetNode(self, pHead):

if pHead == None:

return None

slow = pHead.next

if slow == None:

return None

fast = slow.next

while slow != None and fast != None:

if slow == fast:

return slow

slow = slow.next

fast = fast.next

if slow != fast:

fast = fast.next

return None

编辑于 2018-05-19 14:52:05

回复(3)

6

//时间复杂度:O(n),用map对访问过的节点做标记,这样如果访问一个节点两次,表明找到了环入口,否则没有环。 表示map好好用啊~~

class EntryNodeOfLoop

{

public:

ListNode* EntryNodeOfLoop_Solution(ListNode* pHead)

{

if(pHead==nullptr) return nullptr;

ListNode *ptmp=pHead;

map mlist; //用map关联各个链表指针和对应的映射值

while(ptmp!=nullptr && mlist[ptmp]!=1)

{

mlist[ptmp]=1;//访问过该链表节点,就将实值改为1

ptmp=ptmp->next;

}

if(ptmp==nullptr) return nullptr; //没有环

else return ptmp; //入口节点

}

};

发表于 2017-05-22 22:29:30

回复(3)

6

剑指offer 上面提到了一种很巧妙的方法:

1. 知道环的长度 len

2. 让一个结点先走 len 然后让另一个 和先走的那个一起走,两者必然会相遇。

问题归结为,计算len.

整体流程:

1. 用快慢指针求相遇的结点(如果不存在相遇的结点,返回null 不用后面的计算)

2.  求len

3. 求入口节点。

我们这里使用HashMap , 效率比 ArrayList 效率高点。

/*

public class ListNode {

int val;

ListNode next = null;

ListNode(int val) {

this.val = val;

}

}

*/

import java.util.HashMap;

public class Solution {

public ListNode EntryNodeOfLoop(ListNode pHead){

ListNode node = pHead;

HashMap map = new HashMap<>();

while(node!=null){

if(map.containsKey(node)){

return node;

}else{

map.put(node,true);

node = node.next;

}

}

return null;

}

}

发表于 2016-06-01 16:31:14

回复(3)

2

class Solution:

def EntryNodeOfLoop(self, pHead):

a = []

cur = pHead

while cur:

if cur not in a:

a.append(cur)

cur = cur.next

else:

return cur

return

发表于 2020-02-29 09:54:31

回复(0)

2

# 这样把经过的节点记录下来就可以了呀 class Solution:

def EntryNodeOfLoop(self, pHead):

l = []

p = pHead

res = None

while p:

if p in l:

res = p

break

l.append(p)

p = p.next

return res

编辑于 2019-07-18 09:08:16

回复(1)

2

package entryNodeOfLoop;

import java.util.HashSet;

public class Solution {

public ListNode EntryNodeOfLoop(ListNode pHead) {

if (pHead == null) {

return null;

}

ListNode p = pHead;

HashSet set = new HashSet<>();

while (set.add(p)) {

if (p.next != null) {

p = p.next;

} else {

return null;

}

}

return p;

}

}

发表于 2018-03-10 21:29:48

回复(0)

2

//快慢指针。题目中明明说了有环。可是为何还要判断下?

class Solution {

public:

ListNode* EntryNodeOfLoop(ListNode* pHead)

{

ListNode *slow = pHead;

ListNode *fast = pHead;

do{

if(fast == NULL || fast->next==NULL)

return NULL;

fast = fast->next->next;

slow = slow->next;

}while(slow != fast);

slow = pHead;

while(slow != fast){

slow = slow->next;

fast = fast->next;

}

return slow;

}

};

发表于 2017-06-07 14:48:40

回复(4)

链表中环的入口结点 python_链表中环的入口结点相关推荐

  1. 链表中环的入口结点 python_【Github 5K星】BAT头条滴滴小米等笔试面经+深度学习/算法/NLP资源汇总!...

    最近,在GitHub上有位id为imhuay的热心人带头建立了一个关于国内知名互联网企业笔试和面试经验的资源库,光从名称上就能看出其内容有多丰富:<2018/2019/校招/春招/秋招/算法/机 ...

  2. 《剑指offer》-- 两个链表的第一个公共结点、链表中环的入口结点、删除链表中的重复结点

    一.两个链表的第一个公共结点: 1.题目: 输入两个链表,找出它们的第一个公共结点. 2.解题思路: (1)第一种:找出两个链表的长度,然后让长的链表先走两个链表的长度差,接着两个链表一起走. (2) ...

  3. 链表2--JZ25复杂链表的复制JZ36两个链表的第一个公共结点JZ55链表中环的入口结点JZ56删除链表中重复的结点

    JZ25复杂链表的复制 >>点击此链接 JZ36两个链表的第一个公共结点 题目描述 输入两个无环的单链表,找出它们的第一个公共结点.(注意因为传入数据是链表,所以错误测试数据的提示是用其他 ...

  4. 剑指offer(C++)-JZ23:链表中环的入口结点(数据结构-链表)

    作者:翟天保Steven 版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处 题目描述: 给一个长度为n链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null. ...

  5. 建立一个按年龄排序的有序链表,每个结点包括学号、姓名、性别、年龄。建立一个新的结点,通过年龄将此结点插入到链表中去,使之仍然有序

    <程序设计基础实训指导教程-c语言> ISBN 978-7-03-032846-5 p143 7.1.2 上级实训内容 [实训内容12]建立一个按年龄排序的有序链表,每个结点包括学号.姓名 ...

  6. 反转链表:输入一个链表的头结点,反转该链表并输出反转后的链表的头结点。...

    2019独角兽企业重金招聘Python工程师标准>>> 题目:定义一个函数,输入一个链表的头结点,反转该链表并输出反转后的链表的头结点.     为了正确的反转一个链表,需要调整链表 ...

  7. 无头结点单链表的逆置_单链表的增删查 逆置 倒数第k个节点等问题

    对于单链表而言,它没有双链表那么复杂,它只有头节点,尾节点,节点数据,后继指针.在下面本人实现了 单链表的 增   删   插  查  改. #include #include #include #i ...

  8. 用结点实现链表LinkedList,用数组和结点实现栈Stack,用数组和结点链表实现队列Queue

    一,用结点实现链表LinkedList,不用换JavaAPI的集合框架 import java.util.Scanner;public class Main {public static class ...

  9. 数据结构:(翻转二叉树) 若二叉树采用二叉链表作存储结构,要交换其所有分支结点的左右子树的位置,采用()遍历方法最合适

    题目 若二叉树采用二叉链表作存储结构,要交换其所有分支结点的左右子树的位置,采用()遍历方法最合适?(北京航空航天大学1999,北京工业大学2016) A. 前序 B. 中序 C. 后序 D. 层次 ...

最新文章

  1. 青少年编程竞赛交流群第048次活动录播
  2. POJ 1305 Fermat vs. Pythagoras【勾股数】
  3. mysql数据库自动转储_mysql数据库数据定时封装转储
  4. 拓扑排序 确定比赛名次
  5. AndroidStudio安卓原生开发_Activity的概念和简单使用_创建Activity_创建Layout资源文件_给Activity引入资源Id---Android原生开发工作笔记80
  6. 使用extract-text-webpack-plugin提取css文件
  7. window7 64位 myeclipse9.0破解步骤
  8. 容器技术Docker K8s 30 容器服务ACK基础与进阶-弹性伸缩
  9. 做科普自媒体是怎么挣钱的?
  10. 西北大学计算机课表,西北大学课表
  11. 手写linux系统,在Linux操作系统中使用手写板
  12. 什么是表压?什么是绝压?表压和绝压什么关系?
  13. 【读后感1】读《我是一只it小小鸟》有感
  14. python下标访问字典的指_python字典下标
  15. 民办三本,我从3K到15K的一年
  16. RabbitMQ基础学习
  17. Nginx的配置与优化
  18. 6GK5116-0BA00-2AB2的技术参数说明
  19. HTML CSS JS基础
  20. html设计动画小黄人,纯CSS3画出小黄人并实现动画效果_html/css_WEB-ITnose

热门文章

  1. 中文分词词性对照表(转)
  2. mysql event scheduler机制 与 动态表名创建
  3. 高级工程师必须精通的七种武器(摘录)
  4. WebBrowser页面与WinForm交互技巧
  5. vba 单元格 一系例操作
  6. 明早1点去青岛,可能要两天不能写博客了
  7. C++又一坑:动态链接库中的全局变量
  8. FreeRTOS的中断优先级配置小结
  9. ESP32中下载固件时的波特率设置和调试监控时波特率的设置
  10. ON_MESSAGE,ON_COMMAND和ON_NOTIFY的区别和联系