【LeetCode】Top100 经典必刷题 05【5/20】

21. 合并 K 个升序链表

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

 

示例 1:

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
  1->4->5,
  1->3->4,
  2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6

示例 2:

输入:lists = []
输出:[]

示例 3:

输入:lists = [[]]
输出:[]

 

提示:

  • k == lists.length
  • 0 <= k <= 10^4
  • 0 <= lists[i].length <= 500
  • -10^4 <= lists[i][j] <= 10^4
  • lists[i]升序 排列
  • lists[i].length 的总和不超过 10^4

python 参考题解:

第 21 道题是 "Merge k Sorted Lists",题目描述如下: 合并 k 个已排序的链表并返回一个新的已排序的链表。

示例 1:

输入:
[
  1->4->5,
  1->3->4,
  2->6
]
输出: 1->1->2->3->4->4->5->6

以下是用 Python 给出的题解:

# 定义链表节点
class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None
def mergeKLists(lists):
    # 创建一个空栈,用于存储链表的头节点
    stack = []
    
    # 遍历每个链表,将链表的头节点添加到栈中
    for head in lists:
        if head:
            stack.append(head)
    
    # 初始化结果链表的头节点
    dummy = ListNode(0)
    current = dummy
    
    # 遍历栈,合并链表
    while stack:
        # 取出栈顶的链表头节点
        min_head = stack.pop(0)
        # 将最小头节点连接到结果链表中
        current.next = min_head
        current = current.next
        # 如果最小头节点的下一个节点不为空,则将下一个节点添加到栈中
        if min_head.next:
            stack.append(min_head.next)
    
    # 返回结果链表的头节点
    return dummy.next
# 示例
lists = [
    ListNode(1, ListNode(4, ListNode(5))),
    ListNode(1, ListNode(3, ListNode(4))),
    ListNode(2, ListNode(6))
]
result = mergeKLists(lists)
while result:
    print(result.val, end=" -> ")
    result = result.next
# 输出: 1 -> 1 -> 2 -> 3 -> 4 -> 4 -> 5 -> 6

解题思路和步骤:

  1. 创建栈:首先,我们创建一个空栈 stack,用于存储链表的头节点。
  2. 遍历链表:使用一个循环遍历每个链表,将链表的头节点添加到栈中。
  3. 初始化结果链表:创建一个哑节点 dummy 作为结果链表的头节点,并初始化一个指针 current 指向哑节点。
  4. 遍历栈:使用一个循环遍历栈,合并链表。
  5. 取出最小头节点:在循环中,我们取出栈顶的链表头节点 min_head
  6. 连接最小头节点:将最小头节点连接到结果链表中。
  7. 更新指针:每次连接完成后,我们将 current 指针向前移动到新连接的节点。
  8. 添加下一个节点:如果最小头节点的下一个节点不为空,则将下一个节点添加到栈中。
  9. 返回结果链表:最后,我们返回哑节点的下一个节点,即新链表的头节点。

这个算法的时间复杂度是 O(k * n),其中 k 是链表的数量,n 是链表的平均长度。空间复杂度是 O(k),因为我们使用了栈来存储链表的头节点。

22. 两两交换链表中的节点

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

 

示例 1:

输入:head = [1,2,3,4]
输出:[2,1,4,3]

示例 2:

输入:head = []
输出:[]

示例 3:

输入:head = [1]
输出:[1]

 

提示:

  • 链表中节点的数目在范围 [0, 100]
  • 0 <= Node.val <= 100

python 参考题解:

第 22 道题是 "Swap Nodes in Pairs",题目描述如下: 给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。

示例 1:

输入: 1->2->3->4
输出: 2->1->4->3

以下是用 Python 给出的题解:

# 定义链表节点
class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None
def swapPairs(head: ListNode) -> ListNode:
    # 创建一个哑节点作为新链表的头节点
    dummy = ListNode(0)
    dummy.next = head
    current = dummy
    
    # 遍历链表,交换相邻的节点
    while current.next and current.next.next:
        # 保存第一个节点的下一个节点
        next_node = current.next.next
        # 交换第一个节点和第二个节点
        current.next.next = current.next.next.next
        next_node.next = current.next
        current.next = next_node
        # 移动到下一个交换对
        current = current.next.next
        
    # 返回新链表的头节点
    return dummy.next
# 示例
head = ListNode(1)
head.next = ListNode(2)
head.next.next = ListNode(3)
head.next.next.next = ListNode(4)
result = swapPairs(head)
while result:
    print(result.val, end=" -> ")
    result = result.next
# 输出: 2 -> 1 -> 4 -> 3

解题思路和步骤:

  1. 创建哑节点:首先,我们创建一个哑节点 dummy 作为新链表的头节点,并初始化一个指针 current 指向哑节点。
  2. 遍历链表:使用一个循环遍历链表,交换相邻的节点。
  3. 保存下一个节点:在循环中,我们保存第一个节点的下一个节点 next_node
  4. 交换节点:交换第一个节点和第二个节点。
  5. 更新指针:每次交换完成后,我们将 current 指针向前移动到新交换的节点。
  6. 返回结果链表:最后,我们返回哑节点的下一个节点,即新链表的头节点。

这个算法的时间复杂度是 O(n),其中 n 是链表的长度,因为我们只需要遍历链表一次。空间复杂度是 O(1),因为我们只使用了常数级别的额外空间。

23. K 个一组翻转链表

给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。

k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。

 

示例 1:

输入:head = [1,2,3,4,5], k = 2
输出:[2,1,4,3,5]

示例 2:

输入:head = [1,2,3,4,5], k = 3
输出:[3,2,1,4,5]

 

提示:
  • 链表中的节点数目为 n
  • 1 <= k <= n <= 5000
  • 0 <= Node.val <= 1000

 

进阶:你可以设计一个只用 O(1) 额外内存空间的算法解决此问题吗?

    python 参考题解:

    第 23 道题是 "Reverse Nodes in k-Group",题目描述如下: 给定一个链表和整数 k,请你对链表进行分块反转,其中每个块包含 k 个节点。

    示例 1:

    输入: 1->2->3->4->5, k = 2
    输出: 2->1->4->3->5
    

    以下是用 Python 给出的题解:

    # 定义链表节点
    class ListNode:
        def __init__(self, x):
            self.val = x
            self.next = None
    def reverseKGroup(head: ListNode, k: int) -> ListNode:
        # 创建一个哑节点作为新链表的头节点
        dummy = ListNode(0)
        dummy.next = head
        current = dummy
        
        # 遍历链表,找到需要反转的节点
        while current.next:
            # 初始化前驱节点和当前节点
            prev = current
            curr = current.next
            
            # 遍历 k 个节点,找到需要反转的最后一个节点
            for _ in range(k):
                if not curr:
                    # 如果 k 个节点不足以构成一个完整的块,返回
                    return dummy.next
                curr = curr.next
            
            # 反转当前块
            for _ in range(k):
                next_node = curr.next
                curr.next = prev.next
                prev.next = curr
                curr = next_node
                
            # 更新当前节点
            current.next = prev.next
            
        # 返回新链表的头节点
        return dummy.next
    # 示例
    head = ListNode(1)
    head.next = ListNode(2)
    head.next.next = ListNode(3)
    head.next.next.next = ListNode(4)
    head.next.next.next.next = ListNode(5)
    result = reverseKGroup(head, 2)
    while result:
        print(result.val, end=" -> ")
        result = result.next
    # 输出: 2 -> 1 -> 4 -> 3 -> 5
    

    解题思路和步骤:

    1. 创建哑节点:首先,我们创建一个哑节点 dummy 作为新链表的头节点,并初始化一个指针 current 指向哑节点。
    2. 遍历链表:使用一个循环遍历链表,找到需要反转的节点。
    3. 初始化前驱节点和当前节点:在循环中,我们初始化前驱节点 prev 和当前节点 curr
    4. 遍历 k 个节点:使用一个循环遍历 k 个节点,找到需要反转的最后一个节点。
    5. 反转当前块:使用一个循环反转当前块。
    6. 更新当前节点:每次反转完成后,我们将 current 指针向前移动到新反转的块的最后一个节点。
    7. 返回结果链表:最后,我们返回哑节点的下一个节点,即新链表的头节点。
      这个算法的时间复杂度是 O(n),其中 n 是链表的长度,因为我们只需要遍历链表一次。空间复杂度是 O(1),因为我们只使用了常数级别的额外空间。

    24. 旋转图像

    给定一个 × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。

    你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。

     

    示例 1:

    输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
    输出:[[7,4,1],[8,5,2],[9,6,3]]
    

    示例 2:

    输入:matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]]
    输出:[[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]]
    

     

    提示:

    • n == matrix.length == matrix[i].length
    • 1 <= n <= 20
    • -1000 <= matrix[i][j] <= 1000

     

    python 参考题解:

    第 24 道题是 "Rotate Image",题目描述如下: 给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。

    示例 1:

    Given input matrix = 
    [
      [1,2,3],
      [4,5,6],
      [7,8,9]
    ],
    Output:
    [
      [7,4,1],
      [8,5,2],
      [9,6,3]
    ]
    

    以下是用 Python 给出的题解:

    def rotate(matrix):
        # 先转置矩阵
        matrix[:] = map(list, zip(*matrix))
        # 然后反转每一行
        for i in range(len(matrix)):
            matrix[i] = matrix[i][::-1]
    # 示例
    matrix = [
      [1,2,3],
      [4,5,6],
      [7,8,9]
    ]
    rotate(matrix)
    for row in matrix:
        print(row)
    # 输出:
    # [7,4,1]
    # [8,5,2]
    # [9,6,3]
    

    解题思路和步骤:

    1. 转置矩阵:首先,我们使用 Python 的内置函数 ziplist 来实现矩阵的转置。转置矩阵意味着将原始矩阵的行转换为列,列转换为行。
    2. 反转每一行:然后,我们使用 Python 的切片操作符 [::-1] 来反转每一行。这意味着从最后一个元素开始,到第一个元素结束,步长为 -1,即从右到左遍历每一行。
    3. 返回结果:转置和反转完成后,原始矩阵已经被旋转了 90 度,所以不需要返回任何值。

    这个算法的时间复杂度是 O(n^2),其中 n 是矩阵的边长,因为我们需要遍历矩阵的每一行和每一列。空间复杂度是 O(1),因为我们只使用了常数级别的额外空间。

    25. 反转链表
    给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

     

    示例 1:

    输入:head = [1,2,3,4,5]
    输出:[5,4,3,2,1]
    

    示例 2:

    输入:head = [1,2]
    输出:[2,1]
    

    示例 3:

    输入:head = []
    输出:[]
    

     

    提示:

    • 链表中节点的数目范围是 [0, 5000]
    • -5000 <= Node.val <= 5000

     

    进阶:链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?

    python 参考题解:

    第 206 道题是 "Reverse Linked List",题目描述如下: 反转一个单链表。

    示例 1:

    输入: 1->2->3->4->5->NULL
    输出: 5->4->3->2->1->NULL
    

    以下是用 Python 给出的题解:

    # 定义链表节点
    class ListNode:
        def __init__(self, x):
            self.val = x
            self.next = None
    def reverseList(head: ListNode) -> ListNode:
        # 初始化三个指针,pre指向None,cur指向head,nex指向cur的下一个节点
        pre, cur, nex = None, head, head
        
        # 遍历链表,进行反转
        while cur:
            nex = cur.next  # 保存cur的下一个节点
            cur.next = pre  # 将cur的next指针指向pre
            pre = cur       # 移动pre到cur的位置
            cur = nex       # 移动cur到nex的位置
            
        # 反转完成后,pre指向新的头节点
        return pre
    # 示例
    head = ListNode(1)
    head.next = ListNode(2)
    head.next.next = ListNode(3)
    head.next.next.next = ListNode(4)
    head.next.next.next.next = ListNode(5)
    result = reverseList(head)
    while result:
        print(result.val, end=" -> ")
        result = result.next
    # 输出: 5 -> 4 -> 3 -> 2 -> 1 -> NULL
    

    解题思路和步骤:

    1. 初始化指针:首先,我们创建三个指针 precurnexpre 初始化为 Nonecur 初始化为链表的头节点 headnex 初始化为 cur 的下一个节点。
    2. 遍历链表:使用一个循环遍历链表,进行反转操作。
    3. 保存下一个节点:在循环中,我们首先保存 cur 的下一个节点 nex
    4. 反转当前节点:然后,我们将 curnext 指针指向 pre,实现反转。
    5. 移动指针:接着,我们将 pre 移动到 cur 的位置,将 cur 移动到 nex 的位置。
    6. 返回新头节点:遍历完成后,pre 指向新的头节点,我们返回 pre

    这个算法的时间复杂度是 O(n),其中 n 是链表的长度,因为我们只需要遍历链表一次。空间复杂度是 O(1),因为我们只使用了常数级别的额外空间。

    • 20
      点赞
    • 5
      收藏
      觉得还不错? 一键收藏
    • 0
      评论
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值