2023-05-23 16:15阅读: 10评论: 0推荐: 0

链表应用 III

链表

链表相关的典型应用如下:

应用

应用1:Leetocde 21. 合并两个有序链表

题目

21. 合并两个有序链表

分析

定义一个虚节点 dummy,然后开始遍历两个链表,每次将两个链表中较小的节点连接到新链表上,并后移动当前链表的指针。

遍历完后,如果两个链表的长度不相等,将剩余链表直接连接到新链表上。

代码实现

方法一:迭代实现

class Solution:
def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
return self.merge(list1, list2)
def merge(self, left: ListNode, right: ListNode):
""" 迭代实现 """
dummy = ListNode(-1)
node = dummy
while left and right:
if left.val < right.val:
node.next = left
left = left.next
else:
node.next = right
right = right.next
node = node.next
if not left:
node.next = right
else:
node.next = left
return dummy.next

方法一:递归实现

class Solution:
def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
return self.merge(list1, list2)
def merge(self, left: ListNode, right: ListNode):
""" 递归实现 """
if not left:
return right
if not right:
return left
if left.val < right.val:
left.next = self.merge(left.next, right)
return left
else:
right.next = self.merge(left, right.next)
return right

应用2:Leetocde 23. 合并 K 个升序链表

题目

23. 合并 K 个升序链表

分析

方法一:分治

使用分治的思想,将原链表数组拆分,将多个链表的合并,转换为合并两个链表的问题。

方法二:优先级队列

代码实现

把链表的头结点放进一个小根堆,这样堆顶的元素始终是最小的元素,只需要每次弹出一个元素,即最小元素,如果它还有后驱节点,则将其放回小根堆。

方法一:分治

class Solution:
def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
return self.merge(lists, 0, len(lists) - 1)
def merge(self, lists, left: int, right: int):
if left == right:
return lists[left]
if left > right:
return None
mid = (left + right) // 2
return self.merge_2_lists(self.merge(lists, left, mid), self.merge(lists, mid + 1, right))
def merge_2_lists(self, left: ListNode, right: ListNode):
if left is None:
return right
if right is None:
return left
dummy = ListNode()
p = dummy
while left and right:
if left.val < right.val:
p.next = left
left = left.next
else:
p.next = right
right = right.next
p = p.next
if left:
p.next = left
else:
p.next = right
return dummy.next

方法二:优先级队列

class Solution {
ListNode mergeKLists(ListNode[] lists) {
if (lists.length == 0) {
return null;
}
// 虚拟头结点
ListNode dummy = new ListNode(-1);
ListNode p = dummy;
// 优先级队列,最小堆
PriorityQueue<ListNode> pq = new PriorityQueue<>(
lists.length, (a, b)->(a.val - b.val)
);
// 将 k 个链表的头结点加入最小堆
for (ListNode head : lists) {
if (head != null)
pq.add(head);
}
while (!pq.isEmpty()) {
// 获取最小节点,接到结果链表中
ListNode node = pq.poll();
p.next = node;
// 如果当前节点还有next节点,将其放回小根堆
if (node.next != null) {
pq.add(node.next);
}
// p 指针不断前进
p = p.next;
}
return dummy.next;
}
}

应用3:Leetocde 141. 环形链表

题目

141. 环形链表

分析

使用快慢指针即可,慢指针走一步,快指针走两步,如果快慢指针相遇,则链表存在环,否则就不存在环。

代码实现

class Solution:
def hasCycle(self, head: Optional[ListNode]) -> bool:
if not head or not head.next:
return False
slow = head
fast = head.next
while slow != fast:
if not fast or not fast.next:
return False
slow = slow.next
fast = fast.next.next
return True

应用4:Leetocde 142. 环形链表 II

题目

142. 环形链表 II

分析

算法步骤:

  • 使用快慢指针 slowfast,分别指向链表的头节点;

  • 遍历链表,慢指针走一步,快指针走两步,当两者相遇时,则停止遍历;

    • 如果快指针 fast 指向空节点或者 fast 的下一个节点为空节点,则不存在环;

    • 否则,重新让慢指针 slow 指向头节点,fast 指针从当前位置开始移动,如果两者相遇,则相遇的位置就是环的起点。

代码实现

public class Solution {
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) {
// fast 遇到空指针说明没有环
return null;
}
// 重新指向头结点
slow = head;
// 快慢指针同步前进,相交点就是环起点
while (slow != fast) {
fast = fast.next;
slow = slow.next;
}
return slow;
}
}

应用5:Leetocde 876. 链表的中间结点

题目

876. 链表的中间结点

分析

使用快慢指针的思路,即慢指针走一步,快指针走两步,当快指针走到链表末尾时,慢指针就指向链表中点。

代码实现

class Solution {
public ListNode middleNode(ListNode head) {
// 快慢指针初始化指向 head
ListNode slow = head, fast = head;
// 快指针走到末尾时停止
while (fast != null && fast.next != null) {
// 慢指针走一步,快指针走两步
slow = slow.next;
fast = fast.next.next;
}
// 慢指针指向中点
return slow;
}
}

应用6:Leetocde 160. 相交链表

题目

160. 相交链表

分析

以题目中的例子为例:
image

这里,如果我们简单使用双指针的思路遍历两个链表,由于两个链表的长度不同,可能无法同时到达公共节点 C1

image

算法步骤:

  • 使用两个指针 p1p2 分别指向链表 A 的头节点和链表 B 的头节点;

  • 同时开始遍历两个链表,并且

    • p1 遍历完链表 A 后,立即指向链表 B 的头节点;

    • p2 遍历完链表 B 后,立即指向链表 A 的头节点;

  • 当指针 p1p2相遇时,则当前节点为相交链表的公共节点。

image

这里,我们假设链表 A 头节点到公共节点的长度为 LA0,链表 A 的长度为 LA,链表 B 头节点到公共节点的长度为 LB0,链表B的长度为 LB

当指针 p1p2相遇时,一共走过的长度 L 为:

L=LA+LB0=LB+LA0

代码实现

class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
if not headA or not headB:
return None
p1 = headA
p2 = headB
while p1 != p2:
p1 = headB if not p1 else p1.next
p2 = headA if not p2 else p2.next
return p1

应用7:Leetocde 19. 删除链表的倒数第 N 个结点

题目

19. 删除链表的倒数第 N 个结点

分析

算法思路:

  • 先通过双指针的思路,找到倒数第 n+1 个节点,即待删除节点的前一个节点;

    查找倒数的 k 个节点的思路:

    • 使用两个指针p1p2,同时指向头节点;

    • 先让 p1 先移动 k 步;

    • p1p2 同时后移,直到 p1 移动到链表末尾。

  • 删除倒数第 n+1 个节点的下一个节点;

代码实现

class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
if not head:
return head
dummy = ListNode(-1, head)
# 找到倒数第n+1个节点
node = self.findFromEnd(dummy, n + 1)
node.next = node.next.next
return dummy.next
def findFromEnd(self, head: ListNode, k) -> ListNode:
""" 找到链表的倒数第k个节点 """
p1 = head
# p1先移动k步
for i in range(k):
p1 = p1.next
p2 = head
# p2移动 n - k 步
while p1:
p1 = p1.next
p2 = p2.next
return p2
def removeNthFromEnd2(self, head: ListNode, n: int) -> ListNode:
dumy = ListNode(-1, head)
fast, slow = head, dumy
for i in range(n):
fast = fast.next
while fast:
fast = fast.next
slow = slow.next
slow.next = slow.next.next
return dumy.next

参考:

本文作者:LARRY1024

本文链接:https://www.cnblogs.com/larry1024/p/17425167.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   LARRY1024  阅读(10)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.