单链表的中点
package com.algorithm202305.linkedlist;
/**
* https://leetcode.cn/problems/middle-of-the-linked-list/submissions/432318762/
* 单链表的中点
* 问题的关键也在于我们无法直接得到单链表的长度 n,常规方法也是先遍历链表计算 n,再遍历一次得到第 n / 2 个节点,也就是中间节点。
*
* 如果想一次遍历就得到中间节点,也需要耍点小聪明,使用「快慢指针」的技巧:
*
* 我们让两个指针 slow 和 fast 分别指向链表头结点 head。
*
* 每当慢指针 slow 前进一步,快指针 fast 就前进两步,这样,当 fast 走到链表末尾时,slow 就指向了链表中点。
*/
public class Demo5 {
public static class ListNode {
int val;
ListNode next;
ListNode() {
}
ListNode(int val) {
this.val = val;
}
ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}
}
ListNode middleNode(ListNode head){
//快慢指针指向head
ListNode slow = head,fast = head;
while (slow != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
}
链表是否成环+环的起点
每当慢指针 slow
前进一步,快指针 fast
就前进两步。
如果 fast
最终遇到空指针,说明链表中没有环;如果 fast
最终和 slow
相遇,那肯定是 fast
超过了 slow
一圈,说明链表中含有环。
只需要把寻找链表中点的代码稍加修改就行了:
package com.algorithm202305.linkedlist;
/**
* 1 判断链表是否成环(环形链表)
* https://leetcode.cn/problems/linked-list-cycle/
* 2 环的起点(环形链表Ⅱ)
* https://leetcode.cn/problems/linked-list-cycle-ii/
* 判断链表是否包含环属于经典问题了,解决方案也是用快慢指针:
*
*/
public class Demo6 {
class ListNode {
int val;
ListNode next;
}
boolean hasCycle(ListNode head) {
ListNode slow = head, fast = head;
while (slow != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
//fast有是slow的两倍速存在环应该有相等的节点
if (fast == slow) {
return true;
}
}
return false;
}
//环的起点
public ListNode detectCycle(ListNode head) {
ListNode fast, slow;
fast = slow = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) break;
}
if(fast == null || fast.next == null){
return null;
}
//慢指针重新指向头节点
slow = head;
//快指针比慢指针要比慢指针多走K步,设置起点距离相遇点是m步,那么对于k-m就是环起点
while (slow != fast){
fast = fast.next;
slow = slow.next;
}
return slow;
}
}
我们假设快慢指针相遇时,慢指针 slow
走了 k
步,那么快指针 fast
一定走了 2k
步:
fast
一定比 slow
多走了 k
步,这多走的 k
步其实就是 fast
指针在环里转圈圈,所以 k
的值就是环长度的「整数倍」。
假设相遇点距环的起点的距离为 m
,那么结合上图的 slow
指针,环的起点距头结点 head
的距离为 k - m
,也就是说如果从 head
前进 k - m
步就能到达环起点。
巧的是,如果从相遇点继续前进 k - m
步,也恰好到达环起点。因为结合上图的 fast
指针,从相遇点开始走k步可以转回到相遇点,那走 k - m
步肯定就走到环起点了:
所以,只要我们把快慢指针中的任一个重新指向 head
,然后两个指针同速前进,k - m
步后一定会相遇,相遇之处就是环的起点了。
环的相遇点和环的起点
假设我们有一个链表:1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 3,其中节点3后面的部分形成了一个环。
首先,我们使用快慢指针算法来判断链表是否有环。我们设置快指针每次走两步,慢指针每次走一步,并从起点开始遍历链表。当快指针追上慢指针时,说明链表中存在环。在本例中,快指针和慢指针在节点6处相遇。
接下来,我们需要区分出环的起点。为此,我们再次使用快慢指针算法,但是这次我们让快指针从相遇点开始走,并且让慢指针回到起点。然后,快指针每次走两步,慢指针每次走一步,直到它们再次相遇。那么它们相遇的节点就是环的起点。
在本例中,我们让慢指针回到起点1,并让快指针从相遇点6开始走。快指针第一步可以到达节点3,第二步到达节点4,第三步到达节点5,第四步又回到了节点3。而慢指针在这个过程中只走了一步,到达了节点2。然后,我们让快指针回到起点6,并且速度变成每次走一步,慢指针也继续每次走一步。当它们再次相遇时,它们相遇的节点就是环的起点,也就是节点3。
因此,在这个例子中,相遇点是节点6,而环的起点是节点3。
链表是否相交
如果用两个指针 p1
和 p2
分别在两条链表上前进,并不能同时走到公共节点,也就无法得到相交节点 c1
。
解决这个问题的关键是,通过某些方式,让 p1
和 p2
能够同时到达相交节点 c1
。
所以,我们可以让 p1
遍历完链表 A
之后开始遍历链表 B
,让 p2
遍历完链表 B
之后开始遍历链表 A
,这样相当于「逻辑上」两条链表接在了一起。
如果这样进行拼接,就可以让 p1
和 p2
同时进入公共部分,也就是同时到达相交节点 c1
:
package com.algorithm202305.linkedlist;
/**
* 两链表是否有相交(相交链表)
* https://leetcode.cn/problems/intersection-of-two-linked-lists/
*/
public class Demo7 {
class ListNode {
int val;
ListNode next;
}
ListNode getIntersectionNode(ListNode headA, ListNode headB) {
// p1 指向 A 链表头结点,p2 指向 B 链表头结点
ListNode p1 = headA, p2 = headB;
while (p1 != p2) {
// p1 走一步,如果走到 A 链表末尾,转到 B 链表
if (p1 == null) p1 = headB;
else p1 = p1.next;
// p2 走一步,如果走到 B 链表末尾,转到 A 链表
if (p2 == null) p2 = headA;
else p2 = p2.next;
}
return p1;
}
}
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.fwqtg.net
一、语法 dmidecode 二、选项 -d:(default:/dev/mem)从设备文件读取信息,输出内容与不加参数标准输出相同。 -h:显示帮助信息。 -s:只显示指定DMI字符串的信息。(string) -t:只显示指定条目的信息。(type) -u:…