问题:如何判断一个单向链表中是否存在环?

例如:

链表中存在环(B-->D):

     <-- <--^
| |
v |
A-->B-->C-->D 链表中不存在环: A-->B-->C-->D-->E-->F

解题思路:

  从一个实际的生活场景出发,两个人,在一个环形的操场上跑步的时候,如果有一个人跑得比另一个人还要快,那么,在n圈之后,这两个人总会在操场上的某个点相遇。将操场类比于链表中存在的环路径,将两个人看成两个指针,那么这道题的解题思路就自然而然的出来了。

具体步骤如下:

  1. 初始化两个指针a,b,同时指向链表的开头
  2. a指针走一步,b指针走两步(我们姑且将a指针称为慢指针,将b指针称为快指针)
  3. 重复步骤2,直到b指针无法继续往下走两步或者a,b指针相遇
  4. 当b指针无法继续往下走两步的时候,说明链表中不存在环,b指针即将走到链表的末尾端。
  5. 当a,b指针相遇,说明链表中存在环,因为只有存在环的情况,a,b指针才有可能会在环中的某个点相遇

具体代码如下:

/**
* @author 学徒
*
* 用于判断链表中是否存在环
*
*/
public class CycleLinkedList {
/**
* 循环链表中的节点类
*/
static class Node<T>{
//节点值
T value;
//节点的下一个节点的指针
Node<T> next;
public Node(T value){
this(value,null);
}
public Node(T value,Node next){
this.next=next;
this.value=value;
}
} /**
* 用于判断该链表中是否存在着环
* @param head 链表的头节点
* 当存在环时,返回true,否则返回false
*/
public boolean judge(Node head) {
if(head==null){
return false;
}
//两个指向头结点的指针
Node a=head,b=head;
while(true){
if(b.next==null||b.next.next==null){
return false;
}
a=a.next;
b=b.next.next;
if(a==b){
return true;
}
} } public static void main(String[] args){
Node<String> a=new Node<String>("A");
Node<String> b=new Node<String>("B");
Node<String> c=new Node<String>("C");
Node<String> d=new Node<String>("D");
a.next=b;
b.next=c;
c.next=d;
d.next=b;
CycleLinkedList list=new CycleLinkedList();
boolean result=list.judge(a);
System.out.println("链表中环的结果:"+result);
}
}

  从上面的链表中是否存在环的问题,可以延伸出与链表中是否存在环相关的另一个问题

问题: 链表中构成环的元素的个数应该如何计算?

  对于这个问题,我们稍微沿着解决上面的链表存在环的问题的思路继续往下想,当链表中存在环的时候,快慢指针相遇,那么这个时候,我们只需要让快指针停留在相遇的位置,让慢指针再次走一遍,边走边记录步数,当快慢指针再次相遇的时候,慢指针所走的步数,便是构成环的链表的环中元素个数。

我们只需要稍微修改下上面的代码即可,具体代码如下:

/**
* @author 学徒
*
* 用于判断链表中是否存在环
*
*/
public class CycleLinkedList {
/**
* 循环链表中的节点类
*/
static class Node<T>{
//节点值
T value;
//节点的下一个节点的指针
Node<T> next;
public Node(T value){
this(value,null);
}
public Node(T value,Node next){
this.next=next;
this.value=value;
}
} /**
* 用于判断该链表中是否存在着环
* @param head 链表的头节点
* 当存在环时,返回环中元素个数,否则返回0
*/
public int judge(Node head) {
if(head==null) {
return 0;
}
//两个指向头结点的指针
Node a=head,b=head;
while(true){
//当出现该情况的时候,说明无环
if(b.next==null||b.next.next==null){
return 0;
}
a=a.next;
b=b.next.next;
//当其存在环
if(a==b){
int number=1;
a=a.next;
while(a!=b){
a=a.next;
number++;
}
return number;
}
} } public static void main(String[] args){
Node<String> a=new Node<String>("A");
Node<String> b=new Node<String>("B");
Node<String> c=new Node<String>("C");
Node<String> d=new Node<String>("D");
a.next=b;
b.next=c;
c.next=d;
d.next=b;
CycleLinkedList list=new CycleLinkedList();
int result=list.judge(a);
System.out.println(result);
}
}

  顺着上面的问题继续的往下想,我们可以延伸出另一个问题

问题:我们是否可以得到第一个进入该链表的环的节点的元素?

  对于该问题,我们可以通过以下的方式得到该节点。

  1. 将快指针重新指向链表的头节点
  2. 快指针和慢指针同时走,当快指针和慢指针相遇时,该节点便是链表中第一个进入环的节点

ps:以上只是一个结论的步骤总结。实际上,可以通过分析得到,在环中,快慢指针第一次相遇时的节点位置与进入环的第一个节点的顺时针方向的距离同链表头节点到进入环中第一个节点的位置的距离相等。

具体代码如下:

/**
* @author 学徒
*
* 用于判断链表中是否存在环
*
*/
public class CycleLinkedList {
/**
* 循环链表中的节点类
*/
static class Node<T>{
//节点值
T value;
//节点的下一个节点的指针
Node<T> next;
public Node(T value){
this(value,null);
}
public Node(T value,Node next){
this.next=next;
this.value=value;
}
} /**
* 用于判断该链表中是否存在着环
* @param head 链表的头节点
* 当存在环时,返回环中元素个数,否则返回0
*/
public Node judge(Node head) {
if(head==null) {
return null;
}
//两个指向头结点的指针
Node a=head,b=head;
while(true){
//当出现该情况的时候,说明无环
if(b.next==null||b.next.next==null){
return null;
}
a=a.next;
b=b.next.next;
//当其存在环
if(a==b){
b=head;
while(a!=b){
a=a.next;
b=b.next;
}
return b;
}
} } public static void main(String[] args){
Node<String> a=new Node<String>("A");
Node<String> b=new Node<String>("B");
Node<String> c=new Node<String>("C");
Node<String> d=new Node<String>("D");
a.next=b;
b.next=c;
c.next=d;
d.next=b;
CycleLinkedList list=new CycleLinkedList();
Node result=list.judge(a);
System.out.println(result.value);
}
}

主目录:

回到目录|·(工)·)

Q:判断链表中是否存在环的相关问题的更多相关文章

  1. <数据结构>XDOJ323.判断有向图中是否有环

    问题与解答 问题描述 判断有向图中是否有环. 输入格式 输入数据第一行是一个正整数,表示n个有向图,其余数据分成n组,每组第一个为一个整数,表示图中的顶点个数n,顶点数不超过100,之后为有向图的邻接 ...

  2. LeetCode -- 推断链表中是否有环

    思路: 使用两个节点.slow和fast,分别行进1步和2步.假设有相交的情况,slow和fast必定相遇:假设没有相交的情况,那么slow或fast必定有一个为null 相遇时有两种可能:1. 仅仅 ...

  3. 查找链表中是否有环linked-list-cycle

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

  4. [Leetcode] Linked list cycle 判断链表是否有环

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

  5. leetcode - 链表两两元素交换 + 判断链表有无环

    链表两两元素交换 给定一个链表,两两交换其中相邻的节点,并返回交换后的链表. 你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换. 示例: 给定 1->2->3->4, 你 ...

  6. [LeetCode] Linked List Cycle II 单链表中的环之二

    Given a linked list, return the node where the cycle begins. If there is no cycle, return null. Foll ...

  7. POJ 1860 Currency Exchange(如何Bellman-Ford算法判断图中是否存在正环)

    题目链接: https://cn.vjudge.net/problem/POJ-1860 Several currency exchange points are working in our cit ...

  8. [LeetCode] 142. Linked List Cycle II 单链表中的环之二

    Given a linked list, return the node where the cycle begins. If there is no cycle, return null. To r ...

  9. 判断链表是否有环(Java实现)

    判断给定的链表中是否有环.如果有环则返回true,否则返回false. 解题思路:设置两个指针,slow和fast,fast每次走两步,slow每次走一步,如果有环的话fast一定会追上slow,判断 ...

随机推荐

  1. libevent源码剖析

    libevent是一个使用C语言编写的,轻量级的开源高性能网络库,使用者很多,研究者也很多.由于代码简洁,设计思想简明巧妙,因此很适合用来学习,提升自己C语言的能力. libevent有这样显著地几个 ...

  2. Python 去除列表中重复的元素

    Python 去除列表中重复的元素 来自比较容易记忆的是用内置的set l1 = ['b','c','d','b','c','a','a'] l2 = list(set(l1)) print l2 还 ...

  3. jvm高级特性(1)(内存泄漏实例)

    jvm内存结构回顾: .8同1.7比,最大的差别就是:元数据区取代了永久代.元空间的本质和永久代类似,都是对JVM规范中方法区的实现. 不过元空间与永久代之间最大的区别在于:元数据空间并不在虚拟机中, ...

  4. php unset变量

    <?php $a="abc"; $b="def"; unset($a,$b); echo $a."\n"; echo $b." ...

  5. leetcode-179-Largest Number(理解规则,自定义cmp函数进行排序)

    题目描述: 给定一组非负整数,重新排列它们的顺序使之组成一个最大的整数. 示例 1: 输入: [10,2] 输出: 210 示例 2: 输入: [3,30,34,5,9] 输出: 9534330 说明 ...

  6. 【GDOI2015】 推箱子 状态压缩+bfs

    请注意$8$是一个美妙的数字 考虑到$8\times 8=64$,而一个unsigned long long是$64$位的,所以考虑用一个$01$状态存储箱子.考虑到箱子能转动,那么四种情况都存一下就 ...

  7. 冒泡排序实现(Java)

    冒泡排序是一种交换排序,它的基本思路是: 两两比较相邻记录的关键字,如果反序则交换,知道没有反序的记录位置. 依次比较相邻的两个数,将小数放在前面,大数放在后面.即在第一趟:首先比较第1个和第2个数, ...

  8. 【学习笔记】linux bash script

    1. sed sed 是一种流编辑器,它是文本处理中非常常用的工具,能够完美的配合正则表达式使用,功能非常强大. mkdir playground touch test.txt echo " ...

  9. 4、xamarin forms 设置安卓的toolbar的高度

    降低学习成本是每个.NET传教士义务与责任. 建立生态,保护生态,见者有份. 今天有群友说 如何调整 toolbar 的 高度. 最初遇到这个问题第一反映就是CustomRender 设置高度借助la ...

  10. Linux的shell script

    Linux的shell script //编辑shell: vi a.sh //子进程运行shell sh a.sh //主线程运行shell source a.sh 相关例子: #!/bin/bas ...