|
| 1 | + # 链表交换专题 |
| 2 | + |
| 3 | + |
| 4 | + |
| 5 | + ## 1. 基础,一个单链表的交换 |
| 6 | + |
| 7 | + 这里以[Leetcode 206](https://leetcode-cn.com/problems/reverse-linked-list/) 为例子. |
| 8 | + |
| 9 | + > 206.反转链表 |
| 10 | + > 反转一个单链表。 |
| 11 | + > |
| 12 | + > 示例: |
| 13 | + > |
| 14 | + > 输入: 1->2->3->4->5->NULL |
| 15 | + > 输出: 5->4->3->2->1->NULL |
| 16 | + > 进阶: |
| 17 | + > 你可以迭代或递归地反转链表。你能否用两种方法解决这道题? |
| 18 | + > 版权由Leetcode所有,如有侵权,请联系删除 |
| 19 | + |
| 20 | + 也是一个很基础的模板 |
| 21 | + |
| 22 | + 首先说第一种思想:直接使用迭代,然后用头插法的方式,再将所有的Node插入新链表中,因为头插法是反向插入的,因此会起到反转的效果 |
| 23 | + |
| 24 | + 这种的思路也很简单 |
| 25 | + |
| 26 | + 1. 创建一个None做链表头。 |
| 27 | + 2. 迭代当前链表,将当前链表的Node插入新链表。 |
| 28 | + |
| 29 | + ```python |
| 30 | + class Solution: |
| 31 | + def reverseList(self, head: ListNode) -> ListNode: |
| 32 | + """ |
| 33 | + 迭代 |
| 34 | + """ |
| 35 | + final_list = None |
| 36 | + while head: |
| 37 | + temp = head.next |
| 38 | + head.next = final_list |
| 39 | + final_list = head |
| 40 | + head = temp |
| 41 | + return final_list |
| 42 | + ``` |
| 43 | + 其中`final_list`是新链表的head位置,初始为NULL,然后使用一个`while`循环去迭代,当前的链表`head`,然后使用头插法一步一步的将Node插入。 |
| 44 | + |
| 45 | + 递归: |
| 46 | + |
| 47 | + 递归的思路主要集中在: |
| 48 | + 1. `reverseList` 将`head.next`部分的链表反转,例如要反转的链表是`[1,2,3,4]`, 首先先将`[2,3,4]`进行反转为`[4, 3, 2]` |
| 49 | + 2. 将`head`放在最后,例如: 然后将`[1]`放在`[4, 3, 2]`的后面`[4,3,2,1]` |
| 50 | + |
| 51 | + > note: 这个思路有一个问题:如何找到2能捕获2的指针从而完成操作`2->next = 1`,最简单的思路是直接去遍历,但是很浪费事件,这里可以直接采用递归体中,先对后面的进行反转,然后再拼接前半部分的顺序,此时`[1]`的next其实依旧指向`[2]`,因此我们可以直接这样捕获,避免重新去查找。 |
| 52 | + |
| 53 | + 形成代码 |
| 54 | + |
| 55 | + ```python |
| 56 | + |
| 57 | + class Solution: |
| 58 | + def reverseList(self, head: ListNode) -> ListNode: |
| 59 | + if head == None or head.next == None: # 防止出现None指针操作 |
| 60 | + return head |
| 61 | + p = self.reverseList(head.next) |
| 62 | + head.next.next = head |
| 63 | + head.next = None |
| 64 | + return p |
| 65 | + ``` |
| 66 | + |
| 67 | + ## 2. 进一步提升,如果要两两替换呢? |
| 68 | + |
| 69 | + 这里使用[Leetcode 24](https://leetcode-cn.com/problems/swap-nodes-in-pairs/)来说明这个问题。 |
| 70 | + |
| 71 | + > 24.两两交换链表中的节点 |
| 72 | + > 给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。 |
| 73 | + > |
| 74 | + > 你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。 |
| 75 | +  |
| 76 | + > |
| 77 | + > 输入:head = [1,2,3,4] |
| 78 | + > 输出:[2,1,4,3] |
| 79 | + > 示例 2: |
| 80 | + > |
| 81 | + > 输入:head = [] |
| 82 | + > 输出:[] |
| 83 | + > 示例 3: |
| 84 | + > |
| 85 | + > 输入:head = [1] |
| 86 | + > 输出:[1] |
| 87 | + > |
| 88 | + > 提示: |
| 89 | + > |
| 90 | + > 链表中节点的数目在范围 [0, 100] 内 |
| 91 | + > 0 <= Node.val <= 100 |
| 92 | + > |
| 93 | + > 来源:力扣(LeetCode) |
| 94 | + > 链接:https://leetcode-cn.com/problems/swap-nodes-in-pairs |
| 95 | + > 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 |
| 96 | + |
| 97 | + 这个依旧分为两个思路:迭代和递归 |
| 98 | + |
| 99 | + 迭代:向后移动两个指针然后交换 |
| 100 | + |
| 101 | + 迭代这里给两个版本 |
| 102 | + 1. 需要使用指针的指针进行操作,因为需要获取到两个节点中前一个节点中的前一个节点的指针,在`python`中一般使用一个临时节点来充当指针的指针例如很多题解中的`dummy = ListNode(-1)`,另一方面也是增加一个头节点方便操作。如果是`C/C++`即可直接使用`**ListNode` |
| 103 | + ```python |
| 104 | + class Solution: |
| 105 | + def swapPairs(self, head: ListNode) -> ListNode: |
| 106 | + head_pointer = ListNode(-1) # 指针的指针,头节点 |
| 107 | + pre = head_pointer |
| 108 | + pre.next = head |
| 109 | + first, second = None, None # 两个节点中的第一个节点和第二个节点 |
| 110 | + while head and head.next: # next 为 null即可退出 |
| 111 | + first = head |
| 112 | + second = head.next |
| 113 | + pre.next = second #交换两节点 |
| 114 | + first.next = second.next |
| 115 | + second.next = first |
| 116 | + |
| 117 | + pre = first #继续遍历后续节点 |
| 118 | + head = first.next |
| 119 | + return head_pointer.next |
| 120 | + |
| 121 | + ``` |
| 122 | + 2. 直接进行值交换 |
| 123 | + ```python |
| 124 | + class Solution: |
| 125 | + def swapPairs(self, head: ListNode) -> ListNode: |
| 126 | + if head == None: |
| 127 | + return head |
| 128 | + |
| 129 | + first, second = head, head.next |
| 130 | + while second != None: |
| 131 | + first.val, second.val = second.val, first.val |
| 132 | + |
| 133 | + first = second.next |
| 134 | + if first == None: |
| 135 | + break |
| 136 | + second = first.next |
| 137 | + |
| 138 | + return head |
| 139 | + ``` |
| 140 | + |
| 141 | + 递归:向前移动两个指针,先将后半部分交换,再将当前位置交换,然后拼接. |
| 142 | + |
| 143 | + ```python |
| 144 | + class Solution: |
| 145 | + def swapPairs(self, head: ListNode) -> ListNode: |
| 146 | + if head == None or head.next == None: |
| 147 | + return head |
| 148 | + next_node = head.next |
| 149 | + head.next = self.swapPairs(next_node.next) |
| 150 | + next_node.next = head |
| 151 | + return next_node |
| 152 | + ``` |
| 153 | + |
| 154 | + |
| 155 | + ## 3. 任意顺序的K个节点交换 |
| 156 | + |
| 157 | + 这里使用,[Leetcode25 K个一组翻转链表](https://leetcode-cn.com/problems/reverse-nodes-in-k-group/)作为例子。 |
| 158 | + |
| 159 | + |
| 160 | + 由于K个交换过于复杂,这里没有采用迭代的思路,因为迭代的思路需要使用栈存储便利后的一些节点。使用递归思路和代码都比较简答。 |
| 161 | + |
| 162 | + 首先将问题分为两个部分: |
| 163 | + 1. K个节点如何交换。这个其实可以转化为问题1,直接套用问题一的模板即可。 |
| 164 | + 2. 判断k个节点的范围。 |
| 165 | + |
| 166 | + ```python |
| 167 | + |
| 168 | + def swap(head: ListNode): |
| 169 | + final_list = None |
| 170 | + while head: |
| 171 | + temp = head.next |
| 172 | + head.next = final_list |
| 173 | + final_list = head |
| 174 | + head = temp |
| 175 | + |
| 176 | + return final_list |
| 177 | + |
| 178 | + class Solution: |
| 179 | + def reverseKGroup(self, head: ListNode, k: int) -> ListNode: |
| 180 | + if k == 1 or head == None: |
| 181 | + return head |
| 182 | + |
| 183 | + end = head |
| 184 | + # 向后找k个节点 |
| 185 | + for _ in range(k-1): |
| 186 | + end = end.next # 向后的 |
| 187 | + if end == None: |
| 188 | + return head |
| 189 | + end_next = end.next |
| 190 | + # end之后置空,方便swap函数进行判断 |
| 191 | + end.next = None |
| 192 | + # 交换一下 |
| 193 | + swap(head) |
| 194 | + # 拼起来 |
| 195 | + head.next = self.reverseKGroup(end_next, k) |
| 196 | + ``` |
| 197 | + |
| 198 | + 时间复杂度为`O(n)`刚好扫描过所有的链表。空间复杂度由于是递归的问题存在一定的递归栈空间复杂度是`O(n/k)` |
0 commit comments