/
Week 41 @ 2024 算法周记【双指针链表】

Week 41 @ 2024 算法周记【双指针链表】

LC 21. Merge Two Sorted Lists 合并两个有序列表

https://leetcode.com/problems/merge-two-sorted-lists/

/** * Definition for singly-linked list. * type ListNode struct { * Val int * Next *ListNode * } */ func mergeTwoLists(list1 *ListNode, list2 *ListNode) *ListNode { dummy := &ListNode{} p := dummy p1 := list1 p2 := list2 for p1 != nil && p2 != nil { if p1.Val < p2.Val { p.Next = p1 p1 = p1.Next p = p.Next } else { p.Next = p2 p2 = p2.Next p = p.Next } } for p1 != nil { p.Next = p1 p1 = p1.Next p = p.Next } for p2 != nil { p.Next = p2 p2 = p2.Next p = p.Next } return dummy.Next }

优化解法

最后去合并剩余的 list1 / list2 时,不需要逐节点合并,只需要让 p.Next 指向最终剩余的 list 即可(其他节点已经「组成好一个有序链表」了)

/** * Definition for singly-linked list. * type ListNode struct { * Val int * Next *ListNode * } */ func mergeTwoLists(list1 *ListNode, list2 *ListNode) *ListNode { dummy := &ListNode{} p := dummy p1 := list1 p2 := list2 for p1 != nil && p2 != nil { if p1.Val < p2.Val { p.Next = p1 p1 = p1.Next p = p.Next } else { p.Next = p2 p2 = p2.Next p = p.Next } } if p1 != nil { p.Next = p1 } if p2 != nil { p.Next = p2 } return dummy.Next }

LC 86. Partition List 分隔链表

https://leetcode.com/problems/partition-list/

题意:看上去返回的是一个链表,实际上是返回了两个链表连起来的。。

相当于生成两个新的链表,一个链表的所有值都 < x,另一个都 >=x ,再将这两个链表合到一起

/** * Definition for singly-linked list. * type ListNode struct { * Val int * Next *ListNode * } */ func partition(head *ListNode, x int) *ListNode { dummy1 := &ListNode{} dummy2 := &ListNode{} p1 := dummy1 p2 := dummy2 for p := head; p != nil; p = p.Next { if p.Val < x { p1.Next = p p1 = p1.Next } else { p2.Next = p p2 = p2.Next } } p1.Next = dummy2.Next p2.Next = nil return dummy1.Next }

LC 23. Merge k Sorted Lists 合并 k 个有序列表

https://leetcode.com/problems/merge-k-sorted-lists/

上述代码时间复杂度为 O(lists.length * max(lists[i].length)),即 O(NM)

考虑条件

  • 0 <= lists[i].length <= 500

  • k == lists.length, 0 <= k <= 10^4

最大为 5 * 10^6 可以通过 OJ

优化方案

将所有优化方案

将所有 list 的头节点加入堆中,时间复杂度降为 O(sum(lists[i].length) * log(lists.length)),即 O(NlogK)

LC 19. Remove Nth Node From End of List 删除链表的倒数第 N 个结点

https://leetcode.com/problems/remove-nth-node-from-end-of-list/

本题重点在于利用双指针找到倒数第 x 个节点

将找到倒数第 x 个节点封装成函数 findFromEnd,题目可以转变为 找到倒数第 n+1 个节点 然后进行操作

LC 876. Middle of the Linked List 链表的中间结点(找到链表的中点)

https://leetcode.com/problems/middle-of-the-linked-list/

快慢指针:奇数时 slow 指向中间节点、偶数时 slow 指向中间两节点中后面那个

进阶问题:如何让偶数时指向前面那个?

答:引入一个额外的 dummy 头节点,与原链组成新链,利用该算法对新链取中间节点即可

  • 原链为奇数 [0, 2p+1) → 新链为偶数 [0, 2p+2) → 取中点获得新链的 p+1 号节点 → 等同原链的 p 号节点

  • 原链为偶数 [0, 2p) → 新链为奇数 [0, 2p+1) → 取中点获得新链的 p 号节点 → 等同原链的 p-1 号节点

LC 2095. Delete the Middle Node of a Linked List 删除链表的中间节点

Delete the Middle Node of a Linked List - LeetCode

Week 41 @ 2024 算法周记【双指针链表】 | Middle of the Linked List 的基础上,记录 slow 前面那个指针

LC 141. Linked List Cycle 环形链表(判断链表是否有环)

Linked List Cycle - LeetCode

LC 142. Linked List Cycle II 环形链表 II (找到环形链表的起点)

Linked List Cycle II - LeetCode

解法说明:快慢指针相遇时,让其中一个指针指回起点,再重新同速行进再次遇到则为环起点

LC 160. Intersection of Two Linked Lists 相交链表(判断两个链表是否相交)

Intersection of Two Linked Lists - LeetCode

👆解法为交替行走,即 p1 走完 A 走 B、p2 走完 B 走 A,目的是让数值上二者走了相同的路径,最终相交于一点

设 A 长 a+m、B 长 b+m,则 p1 走 a+m+b、p2 走 b+m+a 时相交
无相交点即 m=0 的情况,无需特殊处理

解法 2:与上述解法思路相同,但不那么强求一个 for 循环,分别计算两个链表长度找到差值后前后指针即可

解法 3:简单粗暴哈希表

解法 4:将 tail.Next 与 headB 相连,即成一个「链+圈」结构,用 Week 41 @ 2024 算法周记【双指针链表】 | Linked List Cycle II 环形链表 II (找到环形链表的起点) 算法即可解决