class ListNode {
int val;
ListNode next;
ListNode(int x) {
val = x;
next = null;
}
}
class Solution {
public boolean hasCycle(ListNode head) {//这个算法是在判断一个list中是否出现循环的数段,与val有关
//而原题是让判断list本身是否形成了环,与val无关
if(head == null) return false;
boolean res = true;
ListNode i = head;
ListNode j = head;
int k = 1;
int count = 0;//count用于求list的长度
while(i != null){
count++;
i = i.next;
}
if(count % 2 != 0) return false;//长度不是偶数则不可能存在循环
for(k = 1; k <= count / 2; k++){//暴力去找出现循环段的长度,从1到count/2
res = true;//每次判断一个k后让i,j,res初始化
i = head;
j = head;
//while(k != 0){
//j = j.next;
//k--;
//}//注释内的部分改变了k的值,会死循环
for(int m = 0; m < k; m++){//让j后移k个长度
j = j.next;
}
while(j != null){//i,j依次对比
if(i.val != j.val) res = false;
i = i.next;
j = j.next;
}
if(res){//判断了一个k就要确定是否返回结果
return res;
}
}
return res;
}
}
正确理解题意的解法:
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
/*//由于要形成圆环,而且又是list,则只能是尾部指向前面的某个结点
//先找到尾部结点
boolean res = false;
if(head == null) return false;
//ListNode tail = null;
ListNode shift1 = head;
//while(shift.next != null){}//这样遍历可能会死循环,因为尾结点的next可能指向前面某个结点,永远没有null
//所以无法找到尾部结点,只能在next是否为null与是否为前面某个结点之间结合判断
ListNode shift2 = head.next;
while(shift1 != null){
shift2 = shift1.next;
while(shift2 != null){//这么想:如果有list循环,这个while循环肯定不能结束,只能通过return语句来结束
if(shift2 == shift1) return true;//这样永远不可能跳出内循环,因为永远在和head比较,shift1没有移动,该方法无解
shift2 = shift2.next;
System.out.println("inner");
}
shift1 = shift1.next;
System.out.println("outer");
}
return res;*/
boolean res = false;
if(head == null) return false;
ListNode p = head;
ListNode q = head;
//错误解法:(看一下思路作为参考)
/*//p和q首先要拉开差距
q = q.next;
while(p != null && q != null){//上面无解的方法是想从环的起点找环的终点,绕了一圈
//现在换种思路:当有环时,起点和终点必然是相邻的,那么直接找到这两个相邻的点即可
//画个图,确定p在前,q在后,就有了if(p.next == q) return true;
if(p.next == q) return true;//当没有环时会返回true,因为这种情况p.next必定等于q,所以要换种方法,让后面的每次移两步来防止这种情况发生
p = p.next;
q = q.next;
System.out.println("...");
}*/
//所以要换种方法,让后面的每次移两步来防止这种情况发生,前面的还是只移一步,一个循环,总会有p、q相遇的时刻
//正确解法:
//由于q.next.next建立在q.next非空的基础上
while(p != null && q.next != null && q.next.next != null){//上面无解的方法是想从环的起点找环的终点,绕了一圈
//现在换种思路:当有环时,起点和终点必然是相邻的,那么直接找到这两个相邻的点即可
//画个图,确定p在前,q在后,就有了if(p.next == q) return true;
p = p.next;
q = q.next.next;//一个方法:当循环体内需要q.next.next而又不确定是否为null时,把q.next.next加到while控制条件中去
if(p == q) return true;//移了再比较!!!!
//System.out.println("...");
}
//错误解法二:(针对错误解法的【当没有环时会返回true,因为这种情况p.next必定等于q】)
/*q = q.next;
boolean temp = false;
while(p != null && q != null){//上面无解的方法是想从环的起点找环的终点,绕了一圈
//现在换种思路:当有环时,起点和终点必然是相邻的,那么直接找到这两个相邻的点即可
//画个图,确定p在前,q在后,就有了if(p.next == q) return true;
if(p.next == q) {
temp = true;//当没有环时会返回true,因为这种情况p.next必定等于q
break;//想让temp暂时保存true,跳出循环,然后判断是否是没有循环的,但是若有循环,判断时是一个死循环,所以无解
}
p = p.next;
q = q.next;
System.out.println("...");
}*/
//参考解法:
/*while(p.next != null && q.next.next != null){//对比上面的,看看差在哪:上面的p没有移,q移了,所以p和q在同步上
//画个图,确定p在前,q在后
p = p.next;
q = q.next.next;//一个方法:当循环体内需要q.next.next而又不确定是否为null时,把q.next.next加到while控制条件中去
if(p == q) return true;
}*/
//由于要形成圆环,而且又是list,则只能是尾部指向前面的某个结点
//这么想:如果有list循环,这个while循环肯定不能结束,只能通过return语句来结束
//现在跳出了while循环,说明有null,没有环
return res;
}
}
用到辅助容器的解法:
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
//解法二:直接把list的各结点引用存起来,遍历过程中,看在之前是否出现过
//但是用到了辅助空间set
Set<ListNode> nodesSeen = new HashSet<>();
while (head != null) {
if (nodesSeen.contains(head)) {
return true;
} else {
nodesSeen.add(head);
}
head = head.next;
}
return false;
}
}