算法题:链表

本文介绍了四个与链表相关的Python编程题目,包括合并两个有序链表、分割链表、合并K个升序链表、删除链表倒数第N个节点,以及检测链表环的存在和环的起点。还涉及到了快慢指针法解决链表相交问题。
摘要由CSDN通过智能技术生成

ps:中括号[] 中的数字,表示此算法题在 力扣 中的序号,直接搜索序号即可找到原题。

[21]合并两个链表

class Solution(object):
    def mergeTwoLists(self, list1, list2):
        """
        :type list1: Optional[ListNode]
        :type list2: Optional[ListNode]
        :rtype: Optional[ListNode]
        """
        dummy = ListNode(-1)
        p = dummy
        p1 = list1
        p2 = list2

        while p1 and p2:
            if p1.val > p2.val:
                p.next = p2
                p2 = p2.next

            else:
                p.next = p1
                p1 = p1.next
#           p指针不断向后移动
            p = p.next

#         如果某一个链表结束了。直接将剩余的移动到新链表上
        if p1:
            p.next = p1
        if p2:
            p.next = p2

        return dummy.next

[86]分割链表

class Solution(object):
    def partition(self, head, x):
        """
        :type head: ListNode
        :type x: int
        :rtype: ListNode
        """
        dummy1 = ListNode(-1)
        dummy2 = ListNode(-1)
        p1 = dummy1
        p2 = dummy2

        p = head
        while p:
            if p.val >= x:
                p2.next = p
                p2 = p2.next
            else:
                p1.next = p
                p1 = p1.next

            # 切开原链表的每个 next 连接,如果不断开源链表的链接关系,可能会出现环形结构。
            temp = p.next
            p.next = None
            p = temp

        # 重新连接链表
        p1.next = dummy2.next

        return dummy1.next

[23]合并 K 个升序链表

import heapq
class Solution(object):
    def mergeKLists(self, lists):
        """
        :type lists: List[ListNode]
        :rtype: ListNode
        """
        # 难点在于如何在k个元素中快速找到最小的值
        # 这里借助堆这种数据结构,本体采用二叉堆: import heapq
        if not lists:
            return None

        dummy = ListNode(-1)

        # heaqp 最小堆
        pq = []
        for head in lists:
            if head:
                heapq.heappush(pq, (head.val, head)

        while pq:
            # 获取最小的节点
            node = heapq.heappop(qp)[1]
            p.next = node
            if node.next:
                heapq.heappush(pq, (node.next.val, node.next))

            p = p.next

        return dummy.next

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

class Solution(object):
    def removeNthFromEnd(self, head, n):
        """
        :type head: ListNode
        :type n: int
        :rtype: ListNode
        """
        dummy = ListNode(-1)
        dummy.next = head

        # 删除倒数第 n 个,要先找倒数第 n + 1 个节点
        x = self._findFromEnd(dummy, n + 1)

        x.next = x.next.next

        return dummy.next

    # 返回链表的倒数第 k 个节点
    def _findFromEnd(self, head, k):
        p1 = head
        # p1 先走 k 步
        for i in range(k):
            p1 = p1.next
        p2 = head
        # p1 和 p2 同时走 n - k 步
        while p1 != None:
            p2 = p2.next
            p1 = p1.next
        # p2 现在指向第 n - k + 1 个节点,即倒数第 k 个节点
        return p2

[141]判断链表中是否有环

每当慢指针 slow 前进一步,快指针 fast 就前进两步。

如果 fast 最终遇到空指针,说明链表中没有环;如果 fast 最终和 slow 相遇,那肯定是 fast 超过了 slow 一圈,说明链表中含有环。

class Solution(object):
    def hasCycle(self, head):
        """
        :type head: ListNode
        :rtype: bool
        """
        slow, fast = head, head
        while fast and fast.next:
            slow = slow.next
            fast= fast.next
            if slow == fast:
                return True
        return False

[ ]寻找环起点

def detectCycle(head: ListNode) -> ListNode:
    fast, slow = head, head
    while fast and fast.next:
        fast = fast.next.next
        slow = slow.next
        if fast == slow:
            break 
	# 判断是否存在环
    if not fast or not fast.next:
        return None
    slow = head 
	# 让慢指针指向 head, 再次相遇就是环的起点
    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 步肯定就走到环起点了:
在这里插入图片描述

[160]两个链表是否相交

  1. 解法一:将末尾的 node 节点 连接其中一个 head 节点,次问题就转换成了寻找环起点的问题了。

  2. 解法二:先便利 A,在遍历 B ,这样在相交节点,我们就就会进入公共部分,如图所示:

    在这里插入图片描述

# 解法二
class Solution(object):
    def getIntersectionNode(self, headA, headB):
        """
        :type head1, head1: ListNode
        :rtype: ListNode
        """
         # p1 指向 A 链表头结点,p2 指向 B 链表头结点
        p1, p2 = headA, headB
        while p1 != p2:
            # p1 走一步,如果走到 A 链表末尾,转到 B 链表
            if p1 == None:
                p1 = headB
            else:
                p1 = p1.next
            # p2 走一步,如果走到 B 链表末尾,转到 A 链表
            if p2 == None:
                p2 = headA
            else:
                p2 = p2.next
        # 如果相交,返回交点,如果不相交,返回空
        return p1
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值