文章归档算法专题

leetcode链表双指针练习题

21. 合并两个有序链表

用两个指针分别指向链表 A、B 的头节点,每次比较当前节点的值,把较小的节点接到结果链表尾部,再移动对应指针。这里用虚拟头节点简化连接操作,复用原来的节点即可。某条链表为空时结束循环,把另一条链表的剩余部分直接接到结果尾部。

Python
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def mergeTwoLists(
        self, list1: Optional[ListNode], list2: Optional[ListNode]
    ) -> Optional[ListNode]:
        # 使用虚拟头节点,简化问题。
        dummy = ListNode(-1)
        head = dummy
        # 不停比较两个元素当前指向位置的元素大小,然后添加到新列表
        while list1 is not None and list2 is not None:
            if list1.val < list2.val:
                head.next = list1
                list1 = list1.next
            else:
                head.next = list2
                list2 = list2.next
            head = head.next
        # 如果有链表依然不为空,直接添加到新链表即可。
        if list1 is not None:
            head.next = list1
        if list2 is not None:
            head.next = list2
        return dummy.next

86. 分隔链表

题目要求小于 x 的节点出现在大于等于 x 的节点之前,并保持各自的相对顺序。可以用两个虚拟头节点分别组织 D1、D2:从前往后遍历原链表,小于 x 的节点接到 D1 尾部,其余节点接到 D2 尾部。这样各部分的顺序保持不变,最后将 D2 接到 D1 后面即可。

Python
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def partition(self, head: Optional[ListNode], x: int) -> Optional[ListNode]:
        # 创建两个虚拟头节点。
        dummy1 = ListNode(-1)
        dummy2 = ListNode(-1)
        head1, head2 = dummy1, dummy2
        # 遍历链表
        while head is not None:
            # 如果值小于x就接到D1后面,否则就接到D2后面
            if head.val < x:
                head1.next = head
                head1 = head1.next
                head = head.next
                head1.next = None
            else:
                head2.next = head
                head2 = head2.next
                head = head.next
                head2.next = None
        # 把D2链表接到D1链表后面即可
        head1.next = dummy2.next
        return dummy1.next

23. 合并 K 个升序链表

与合并两个链表类似,每次都要取出当前最小的头节点,只是候选变成了 K 个。可以把候选放入最小堆,每次弹出最小节点,接到结果尾部,再把它的后继节点放回堆中。堆里保存“节点值、链表次序、节点”三元组:元组先比较节点值,值相同再比较链表次序,避免直接比较不支持大小关系的 ListNode 对象。每条链表在堆中最多保留一个候选,所以链表次序足以区分这些节点。

Python
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
        dummy = ListNode(-1)
        h = dummy
        pq = []
        for i, node in enumerate(lists):
            if node is not None:
                heapq.heappush(pq, (node.val, i, node))
        while pq:
            val, i, node = heapq.heappop(pq)
            h.next = node
            h = h.next
            if node.next is not None:
                heapq.heappush(pq, (node.next.val, i, node.next))
        return dummy.next

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

找倒数第 N 个节点,可以先遍历求长度,再走到对应位置;也可以只遍历一次。让 p1 先从头节点走 n 步,再让 p2 从头节点出发,两个指针同步前进。p1 到达空节点时,p2 就在倒数第 n 个节点,因为两者始终相差 n 步。

删除节点时,需要找到它的前驱,也就是倒数第 N+1 个节点,再修改 next。这里加入虚拟头节点,并让两个指针从它出发,可以统一处理删除头节点、删除后链表为空等边界情况。

Python
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
        dummy = ListNode(-1)
        dummy.next = head
        p1 = dummy
        p2 = dummy
        for i in range(n + 1):
            p1 = p1.next
        while p1 is not None:
            p1 = p1.next
            p2 = p2.next
        p2.next = p2.next.next
        return dummy.next

876. 链表的中间结点

直观做法是先求链表总长度,再遍历到中间位置。使用快慢指针可以一次完成:slow 和 fast 初始都指向头节点,slow 每次走一步,fast 每次走两步。fast 到达末尾时,slow 刚好走过约一半的距离,停在中间节点。

链表长度为偶数、有两个中间节点时,这种写法返回后面的那个。

Python
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def middleNode(self, head: Optional[ListNode]) -> Optional[ListNode]:
        slow = head
        fast = head
        while fast is not None and fast.next is not None:
            fast = fast.next.next
            slow = slow.next
        return slow

141. 环形链表

继续使用快慢指针。如果链表有环,快指针会在环内追上慢指针;如果快指针先到达空节点,就说明没有环。

Python
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None


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

142. 环形链表 II

先用第 141 题的方法找到相遇点。假设相遇时 slow 走了 k 步,fast 走了 2k 步,那么多走的 k 步一定是环长的整数倍。若 slow 入环后到相遇点走了 m 步,头节点到环入口的距离就是 k-m。从相遇点再走 k-m 步也会回到入口,因为加上已经走过的 m 步,刚好是环长的整数倍。因此,相遇后把 slow 放回头节点,让两者每次都走一步,再次相遇的位置就是环入口。

Python
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None


class Solution:
    def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
        slow, fast = head, head
        while fast != None and fast.next != None:
            fast = fast.next.next
            slow = slow.next
            if fast == slow:
                break
        if fast == None or fast.next == None:
            return None
        slow = head
        while slow != fast:
            fast = fast.next
            slow = slow.next
        return slow

160. 相交链表

两条链表长度可能不同,直接同步向后走不能保证同时进入公共部分。可以让 p1 走完 A 后接着走 B,p2 走完 B 后接着走 A。两条路线的总长度相同,因此能够对齐。这里切换的是遍历指针,并不需要实际修改链表连接。

例如,链表 A 为 a1->a2->c1->c2。

链表 B 为 b1->b2->b3->c1->c2。

按图中的路线交换起点后,两个指针会在共同节点 c1 相遇。如果没有公共节点,两者最终都会走到 None,循环同样结束并返回 None,表示链表不相交。

Python
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:
        p1,p2=headA,headB
        while p1!=p2:
            if p1 is None:
                p1=headB
            else:
                p1=p1.next
            if p2 is None:
                p2=headA
            else:
                p2=p2.next
        return p1
谢谢你读到这里。 ✧
搜索文章

输入关键词,找一篇想读的手记。

算法专题leetcode双指针经典练习题算法题解leetcode-1438. 绝对差不超过限制的最长连续子数组算法题解leetcode-179. 最大数算法题解leetcode-239. 滑动窗口最大值算法题解leetcode-136. 只出现一次的数字算法专题leetcode动态规划基础练习题算法专题leetcode二叉树基础练习题算法专题leetcode数组双指针练习题算法专题leetcode滑动窗口练习题算法专题leetcode链表双指针练习题算法题解洛谷-P1029 [NOIP 2001 普及组] 最大公约数和最小公倍数问题算法题解leetcode-1979. 找出数组的最大公约数算法题解洛谷-B3634 最大公约数和最小公倍数算法题解洛谷-P10424 [蓝桥杯 2024 省 B] 好数算法题解洛谷-P8716 [蓝桥杯 2020 省 AB2] 回文日期自动化脚本Dr.com校园网自动登录方案算法题解leetcode-2226. 每个小孩最多能分到多少糖果算法题解leetcode-2563. 统计公平数对的数目算法题解leetcode-1.两数之和算法题解洛谷-P8218 【深进1.例1】求区间和
在这座小岛上,慢慢找。ESC 关闭