1. 链表中环的问题
这依旧是链表经典问题。
- 判断链表是否有环。Leetcode 141. 环形链表
- 若有环,环的位置何处?Leetcode 142. 环形链表 II
1.1 使用哈希
判断是否有环,最容易的方法是使用hash(一般哈希表都是用来快速判断一个元素是否出现集合里),遍历的时候将元素放入map中,若有环发生碰撞。碰撞即为入口位置
public class Solution {
public boolean hasCycle(ListNode head) {
Set<ListNode> record=new HashSet<>();
ListNode pos=head;
while (pos != null) {
if (record.contains(pos)) {
return true;
}else {
record.add(pos);
}
pos=pos.next;
}
return false;
}
}
1.2 为什么快慢指针一定相遇
fast指针每次移动两个结点,slow指针每次移动一个结点,如果途中相遇,说明该链表有环。
- 假如有一个空格,如上图情况1所示:fast和slow下一步都到3号位置,相遇。
- 加入有两个空格,如上图情况2所示:fast下一步到3,而slow到4,这就变成了情况1——>只要有环,必会相遇。
🏮双指针思想实现寻找是否存在环代码实现:
public boolean hasCycle(ListNode head) {
if(head==null || head.next==null){
return false;
}
ListNode fast=head;
ListNode slow=head;
while(fast!=null && fast.next!=null){//为什么也要判断 fast.next != null?因为快指针每次移动2步,所以要判断它下一个是否为空
fast=fast.next.next;
slow=slow.next;
if(fast==slow)
return true;
}
return false;
}
1.3 确定入口方法
🚀结论:先按照快慢方式寻找到相遇的位置(假如为下图中Z),然后将两指针分别放在链表头(X)和相遇位置(Z),并改为相同速度推进,则两指针在环开始位置相遇(Y)。
结论很简单,但这是为什么呢?
public class Solution {
public ListNode detectCycle(ListNode head) {
//设置快慢指针
ListNode slow=head;
ListNode fast=head;
//快慢指针开始移动,快指针每次移动两节点,慢指针每次移动1节点
while (fast != null && fast.next != null) {//为什么也要判断 fast.next != null?因为快指针每次移动2步,所以要判断它下一个是否为空
fast=fast.next.next;
slow=slow.next;
//快慢指针相遇了
if (fast==slow) {//相等说明有环啦~
ListNode index1=fast;//相遇节点 因为这时候快慢指针是一块了
ListNode index2=head;//头结点
//两个指针,从头结点和相遇结点,各走一步,直到相遇,即为环入口(此处证明网上有说明)
while (index1 != index2) {
index1=index1.next;
index2=index2.next;
}
//返回入环第一个节点
return index1;
}
}
return null;
}
}
2. 双向链表
class DoubleNode {
public int data; //数据域
public DoubleNode next; //指向下一个结点
public DoubleNode prev;
public DoubleNode(int data) {
this.data = data;
}
//打印结点的数据域
public void displayNode() {
System.out.print("{" + data + "} ");
}
}