春季实习冲刺计划:链表交换专题

1. 基础,一个单链表的交换

这里以Leetcode 206 为例子.

206.反转链表
反转一个单链表。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?
版权由Leetcode所有,如有侵权,请联系删除

也是一个很基础的模板

首先说第一种思想:直接使用迭代,然后用头插法的方式,再将所有的Node插入新链表中,因为头插法是反向插入的,因此会起到反转的效果

这种的思路也很简单

  1. 创建一个None做链表头。
  2. 迭代当前链表,将当前链表的Node插入新链表。
1
2
3
4
5
6
7
8
9
10
11
12
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
"""
迭代
"""
final_list = None
while head:
temp = head.next
head.next = final_list
final_list = head
head = temp
return final_list

其中final_list是新链表的head位置,初始为NULL,然后使用一个while循环去迭代,当前的链表head,然后使用头插法一步一步的将Node插入。

递归:

递归的思路主要集中在:

  1. reverseListhead.next部分的链表反转,例如要反转的链表是[1,2,3,4], 首先先将[2,3,4]进行反转为[4, 3, 2]
  2. head放在最后,例如: 然后将[1]放在[4, 3, 2]的后面[4,3,2,1]

note: 这个思路有一个问题:如何找到2能捕获2的指针从而完成操作2->next = 1,最简单的思路是直接去遍历,但是很浪费事件,这里可以直接采用递归体中,先对后面的进行反转,然后再拼接前半部分的顺序,此时[1]的next其实依旧指向[2],因此我们可以直接这样捕获,避免重新去查找。

形成代码

1
2
3
4
5
6
7
8
9

class Solution:
def reverseList(self, head: ListNode) -> ListNode:
if head == None or head.next == None: # 防止出现None指针操作
return head
p = self.reverseList(head.next)
head.next.next = head
head.next = None
return p

2. 进一步提升,如果要两两替换呢?

这里使用Leetcode 24来说明这个问题。

24.两两交换链表中的节点
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。

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

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

输入:head = []
输出:[]
示例 3:

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

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/swap-nodes-in-pairs
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

这个依旧分为两个思路:迭代和递归

迭代:向后移动两个指针然后交换

迭代这里给两个版本

  1. 需要使用指针的指针进行操作,因为需要获取到两个节点中前一个节点中的前一个节点的指针,在python中一般使用一个临时节点来充当指针的指针例如很多题解中的dummy = ListNode(-1),另一方面也是增加一个头节点方便操作。如果是C/C++即可直接使用**ListNode
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class Solution:
    def swapPairs(self, head: ListNode) -> ListNode:
    head_pointer = ListNode(-1) # 指针的指针,头节点
    pre = head_pointer
    pre.next = head
    first, second = None, None # 两个节点中的第一个节点和第二个节点
    while head and head.next: # next 为 null即可退出
    first = head
    second = head.next
    pre.next = second #交换两节点
    first.next = second.next
    second.next = first

    pre = first #继续遍历后续节点
    head = first.next
    return head_pointer.next

  2. 直接进行值交换
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class Solution:
    def swapPairs(self, head: ListNode) -> ListNode:
    if head == None:
    return head

    first, second = head, head.next
    while second != None:
    first.val, second.val = second.val, first.val

    first = second.next
    if first == None:
    break
    second = first.next

    return head

递归:向前移动两个指针,先将后半部分交换,再将当前位置交换,然后拼接.

1
2
3
4
5
6
7
8
class Solution:
def swapPairs(self, head: ListNode) -> ListNode:
if head == None or head.next == None:
return head
next_node = head.next
head.next = self.swapPairs(next_node.next)
next_node.next = head
return next_node

3. 任意顺序的K个节点交换

这里使用,Leetcode25 K个一组翻转链表作为例子。

由于K个交换过于复杂,这里没有采用迭代的思路,因为迭代的思路需要使用栈存储便利后的一些节点。使用递归思路和代码都比较简答。

首先将问题分为两个部分:

  1. K个节点如何交换。这个其实可以转化为问题1,直接套用问题一的模板即可。
  2. 判断k个节点的范围。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

def swap(head: ListNode):
final_list = None
while head:
temp = head.next
head.next = final_list
final_list = head
head = temp

return final_list

class Solution:
def reverseKGroup(self, head: ListNode, k: int) -> ListNode:
if k == 1 or head == None:
return head

end = head
# 向后找k个节点
for _ in range(k-1):
end = end.next # 向后的
if end == None:
return head
end_next = end.next
# end之后置空,方便swap函数进行判断
end.next = None
# 交换一下
swap(head)
# 拼起来
head.next = self.reverseKGroup(end_next, k)

时间复杂度为O(n)刚好扫描过所有的链表。空间复杂度由于是递归的问题存在一定的递归栈空间复杂度是O(n/k)


春季实习冲刺计划:链表交换专题
https://blog.whickylucky.top/2021/01/10/链表交换/
作者
luyanfcp
发布于
2021年1月10日
许可协议