套路
很大概率需要至少兩根指針來完成純鏈表問題的解決
經常加入額外的頭結點來使鏈表中包括原表頭的所有節點處理方式相同。
由于鏈表訪問只能通過從頭部移動,問題中當前結點與前一個結點之間存在關聯時,指針沒辦法回退,這時往往需要用兩根指針來幫助解決問題
注意點
- 特別注意鏈表頭部和尾部的處理,鏈表指針移動時一定記得時刻注意判空。
目錄
- (返回或刪除)鏈表中倒數第k個結點(雙指針)
- 反轉鏈表(雙指針)
- 兩個鏈表的第一個公共結點(雙指針)
- 鏈表中環的入口結點(雙指針)
- 刪除鏈表中重復的結點(雙指針)(有難度)
鏈表中倒數第k個結點
輸入一個鏈表,輸出該鏈表中倒數第k個結點。
- 最優解:創建兩個指針指向頭節點,讓前面的指針先往后移動k-1步,然后兩指針一起移動直到前面的指針到達末尾,后面的指針所在的位置就是第k個結點。時間復雜度 O(n)
public ListNode FindKthToTail(ListNode head,int k) {
if (head == null || k < 1) {
return null;
}
ListNode front = head, behind = head;
for (int i = 0; i < k - 1; i++) {
front = front.next;
if (front == null) {
return null;
}
}
while (front.next != null) {
front = front.next;
behind = behind.next;
}
return behind;
}
反轉鏈表
輸入一個鏈表,反轉鏈表后,輸出鏈表的所有元素。
public ListNode ReverseList(ListNode head) {
if (head == null) {
return null;
}
ListNode prev = null;
while (head.next != null) {
ListNode next = head.next;
head.next = prev;
prev = head;
head = next;
}
head.next = prev;
return head;
}
兩個鏈表的第一個公共結點
輸入兩個鏈表,找出它們的第一個公共結點。
- 本題默認兩鏈表是無環鏈表
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
if (pHead1 == null || pHead2 == null) {
return null;
}
ListNode p1 = pHead1, p2 = pHead2;
while (p1 != p2) {
p1 = p1 == null ? p1 = pHead2 : p1.next;
p2 = p2 == null ? p2 = pHead1 : p2.next;
}
return p1;
}
鏈表中環的入口結點
一個鏈表中包含環,請找出該鏈表的環的入口結點。
- 快指針一次移動兩步,慢指針一次移動一步,找到在環中的相遇節點。之后快指針回到表頭跟慢指針同步移動,再次相遇點即為環入口結點。
- 證明過程:
假設入環前的鏈表長度是 L,環長度是 C,相遇點在環中相對于環入口的長度差是P,快指針走的圈數是N,那么相遇時快指針比慢指針多走一圈,可以得到如下公式:L + NC + P = (L + (N - 1)C + P)* 2 ,解出(N-2)C = P + L,P+L是環長的整數倍,說明相遇點P還需要走L的路程就可以到達環的入口結點。此時再讓快指針回到表頭跟慢指針一起一步一動,相遇時便可以得到環的入口結點。
public ListNode EntryNodeOfLoop(ListNode pHead) {
if (pHead == null) {
return null;
}
ListNode fast = pHead, slow = pHead;
while (fast.next != null && fast.next.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) {
fast = pHead;
while (fast != slow) {
fast = fast.next;
slow = slow.next;
}
return fast;
}
}
return null;
}
刪除鏈表中重復的結點
在一個排序的鏈表中,存在重復的結點,請刪除該鏈表中重復的結點,重復的結點不保留,返回鏈表頭指針。 例如,鏈表1->2->3->3->4->4->5 處理后為 1->2->5
- 指針當前結點(cur)和前一個結點的結點值(last)或后一個(cur.next)結點的結點值相同,就不加入新鏈表。
public ListNode deleteDuplication(ListNode pHead) {
if (pHead == null) {
return null;
}
ListNode front = new ListNode(-1);
front.next = pHead;
ListNode pre = front, cur = front;
int last = pre.val;
while (cur.next != null) {
cur = cur.next;
if (last != cur.val) {
if (cur.next == null || cur.val != cur.next.val) {
pre.next = cur;
pre = cur;
}
last = cur.val;
}
}
pre.next = null;
return front.next;
}