何为链表中的环?
通常情况下,链表是由多个节点组成的,每一个节点指向下一个节点,链表的最后一个节点会指向 null 。但如果链表中的某个节点指向了某个前节点,那么链表就会形成一个环,当遍历链表的时候,就会进入无限循环。
链表中环的影响
多数情况下,环在链表中被认为是错误的或者是不期望的状态,尤其是在单向链表中。一个节点的 next 指针被指向设置为先前的节点,从而会导致链表的无限循环。这种情况可能是编译错误、不正确的链表操作或数据结构的错误使用导致。 当环存在的时候,主要通常会有如下几个问题:
- 无限遍历:链表通常被用作可以顺序遍历的数据结构。一个环会导致遍历操作陷入无限循环,因为永远到达不了链表的末尾 null。
- 内存泄漏:在某些编程环境中,如果链表中存在环,那么标准的垃圾回收机制可能无法正确清理不再需要的节点,从而导致内存泄漏。
- 数据不易处理:环的存在使得标准的链表操作(如反转、合并、查找等)变得复杂甚至不可能正确执行。
但在某些特定的应用中,环也有一定的作用,例如循环链表,在需要遍历循环元素时非常有用。
如何判断链表中的环
检测重复状态通常是发现无限循环的关键,我们可以通过遍历链表来判断是否有节点被访问了不止一次,对于被访问的节点,我们可以用哈希表和双指针的方法来追踪。
哈希表法
将遍历的每一个节点都放入哈希表中,如果有节点被遍历超过一次,哈希表的特性会做出判断,则可以判断该链表是有环的链表。
/**
* 方法1:通过HashMap判断
*
* @param head
* @return
*/
public static boolean hasCycleByMap(ListNode head) {
Set<ListNode> seen = new HashSet<>();
while (head != null) {
if (!seen.add(head)) {
return true;
}
head = head.next;
}
return false;
}
双指针法
双指针方法就是用经典的快慢指针(快指针一次向前两步,慢指针一次向前一步),如果这个链表有环的话,那么快慢指针遍历N次链表后两个指针一定会相遇,这就像在操场上跑步时候“套圈”的原理一样。
/**
* 方法2 通过双指针实现
*
* @param head
* @return
*/
public static boolean hasCycleByTwoPoint(ListNode head) {
if (head == null || head.next == null) {
return false;
}
ListNode fast = head;
ListNode slow = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow)
return true;
}
return false;
}