题目描述
示例:
方法一:快慢指针
这把方法是同时建立两个指针,开始都指向头结点,然后快指针每次向后遍历个结点,慢指针每次 遍历一个结点,如果链表是循环的,追后快指针会再次和满指针相遇。
利用这一特点,可以给出一种结果。
public boolean hasCycle2(ListNode head) {
if (head == null)
return false;
//快慢两个指针
ListNode slow = head;
ListNode fast = head;
while (fast != null && fast.next != null) {
//慢指针每次走一步
slow = slow.next;
//快指针每次走两步
fast = fast.next.next;
//如果相遇,说明有环,直接返回true
if (slow == fast)
return true;
}
//否则就是没环
return false;
}
你也可以快指针移动三次,慢指针移动一次。~~~~~~~
方法二:删除自身结点
从头结点开始遍历。每经过一个结点,先保存他的下一个结点,然后让他的下一个结点指向它自己。逐渐遍历到末尾,当最后一个结点也只想他自己以后,写一个结点要么为空,要么为头结点,如果为头结点吗,会发现头结点的下一个结点是他自己,可利用这一点。
看图:
代码:
public boolean hasCycle(ListNode head) {
//如果head为空,或者他的next指向为空,直接返回false
if (head == null || head.next == null)
return false;
//如果出现head.next = head表示有环
if (head.next == head)
return true;
ListNode nextNode = head.next;
//当前节点的next指向他自己,相当于把它删除了
head.next = head;
//然后递归,查看下一个节点
return hasCycle(nextNode);
}
方法三:利用循环链表反转后的头节点不变的特性
可根据代码画图理解,为什么循环链表反转后头节点不变
代码:
public ListNode reverseList(ListNode head) {
//新链表
ListNode newHead = null;
while (head != null) {
//先保存访问的节点的下一个节点,保存起来
//留着下一步访问的
ListNode temp = head.next;
//每次访问的原链表节点都会成为新链表的头结点,
//其实就是把新链表挂到访问的原链表节点的
//后面就行了
head.next = newHead;
//更新新链表
newHead = head;
//重新赋值,继续访问
head = temp;
}
//返回新链表
return newHead;
}
public boolean hasCycle(ListNode head) {
ListNode rev = reverseList(head);
if (head != null && head.next != null && rev == head) {
return true;
}
return false;
}
方法四:利用set集合不能存储重复元素的特点
因为set集合不能存储重复元素,所以我们没遍历一个节点就将其存入set集合中,如果存储失败,说明其已经存在,则链表为循环链表。
代码:
public boolean hasCycle1(ListNode head) {
if (head==null||head.next==null){
return false;
}
//利用集合不能存储重复元素的特点
Set<ListNode> seen = new HashSet<ListNode>();
while (head != null) {
if (!seen.add(head)) {
return true;
}
head = head.next;
}
return false;
}