|
| 1 | +# LeetCode 第 24 号问题:两两交换链表中的节点 |
| 2 | + |
| 3 | +> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。 |
| 4 | +> |
| 5 | +> 同步博客:https://www.algomooc.com |
| 6 | +
|
| 7 | +题目来源于 LeetCode 上第 24 号问题:两两交换链表中的节点。 |
| 8 | + |
| 9 | +### 题目描述 |
| 10 | + |
| 11 | +给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。 |
| 12 | + |
| 13 | +**你不能只是单纯的改变节点内部的值**,而是需要实际的进行节点交换。 |
| 14 | + |
| 15 | +**示例:** |
| 16 | + |
| 17 | +``` |
| 18 | +给定 1->2->3->4, 你应该返回 2->1->4->3. |
| 19 | +``` |
| 20 | + |
| 21 | +### 题目解析 - 迭代法 |
| 22 | + |
| 23 | +由题目描述可知需要两两交换, 那么以两个为一组子链表交换指针即可, 在设置一个 **哨兵** 指向交换后的子链表 (或者哨兵提前指向子链表的第二个节点,因为第二个节点交换后就成了第一个节点); 然后让哨兵指向下一组子链表,继续交换,直至最后. |
| 24 | + |
| 25 | +设 **哨兵** 为 节点 `prev`, 子链表第一个节点为 `A`, 第二个节点为 `B`, 第三个节点为 `C`, 那么操作流程如下: |
| 26 | + |
| 27 | +- 终止条件 `head == null && head -> next == null` |
| 28 | + 1. prev -> B ( A -> B -> C ) |
| 29 | + 2. A - > C |
| 30 | + 3. B -> A ( prev -> B -> A -> C ) |
| 31 | + 4. prev -> A |
| 32 | + 5. head -> C |
| 33 | + 6. 循环以上步骤 |
| 34 | + |
| 35 | +### 动画描述 |
| 36 | + |
| 37 | + |
| 38 | + |
| 39 | +### 代码实现 |
| 40 | + |
| 41 | +```javascript |
| 42 | +/** |
| 43 | + * JavaScript描述 |
| 44 | + * 迭代法 |
| 45 | + */ |
| 46 | +var swapPairs = function(head) { |
| 47 | + let dummy = new ListNode(0); |
| 48 | + dummy.next = head; |
| 49 | + |
| 50 | + let prevNode = dummy; |
| 51 | + |
| 52 | + while (head !== null && head.next !== null) { |
| 53 | + // Nodes to be swapped |
| 54 | + let firstNode = head, |
| 55 | + secondNode = head.next; |
| 56 | + // Swapping |
| 57 | + prevNode.next = secondNode; // 放到交换前后都可以 |
| 58 | + firstNode.next = secondNode.next; |
| 59 | + secondNode.next = firstNode; |
| 60 | + // Reinitializing the head and prevNode for next swap |
| 61 | + prevNode = firstNode; |
| 62 | + head = firstNode.next; |
| 63 | + } |
| 64 | + return dummy.next; |
| 65 | +}; |
| 66 | +``` |
| 67 | + |
| 68 | +### 复杂度分析 |
| 69 | + |
| 70 | +- 时间复杂度:**O(N)**,其中 *N* 指的是链表的节点数量 |
| 71 | +- 空间复杂度:**O(1)** |
| 72 | + |
| 73 | +### 题目解析 - 递归 |
| 74 | + |
| 75 | +递归的思路和迭代类似, 都是分组交换. 具体来说这里的递归不是针对一个问题深入进去,而是不断向后推进. |
| 76 | + |
| 77 | +- 每次递归只交换一对节点 |
| 78 | +- 下一次递归则是交换下一对节点 |
| 79 | +- 交换完成后返回第二个节点, 因为它是交换后的子链表新头 |
| 80 | +- 递归完成后返回第一次递归的第二个节点, 这就是新链表的头结点 |
| 81 | + |
| 82 | +**注意:** 不要人肉递归, 更多关注整体逻辑 |
| 83 | + |
| 84 | +示例执行大致流程为: |
| 85 | + |
| 86 | +- 终止条件: `(head == null) || (head.next == null)` |
| 87 | + 1. 1 -> 2 -> 3 -> 4 ( 原始链表 ) |
| 88 | + 2. 1 -> 3 -> 4 |
| 89 | + 3. ( 2 -> 1 ) -> 3 -> 4 ( 第一次递归完成后返回原来的第二个节点, 也就是值为 2 的节点 ) |
| 90 | + 4. 2 -> 1 -> 3 -> null |
| 91 | + 5. 2 -> 1 -> ( 4 -> 3 ) ( 第二次递归完成后返回原来的第二个节点, 也就是值为 4 的节点 ) |
| 92 | + |
| 93 | +### 动画描述 |
| 94 | + |
| 95 | + |
| 96 | + |
| 97 | +### 代码实现 |
| 98 | + |
| 99 | +```javascript |
| 100 | +/** |
| 101 | + * JavaScript描述 |
| 102 | + * 递归法 |
| 103 | + */ |
| 104 | +var swapPairs = function(head) { |
| 105 | + if (head == null || head.next == null) { |
| 106 | + return head; |
| 107 | + } |
| 108 | + // Nodes to be swapped |
| 109 | + let firstNode = head, |
| 110 | + secondNode = head.next; |
| 111 | + // Swapping |
| 112 | + firstNode.next = swapPairs(secondNode.next); |
| 113 | + secondNode.next = firstNode; |
| 114 | + |
| 115 | + return secondNode; |
| 116 | + |
| 117 | +}; |
| 118 | +``` |
| 119 | + |
| 120 | +### 复杂度分析 |
| 121 | + |
| 122 | +- 时间复杂度:**O(N)**,其中 *N* 指的是链表的节点数量 |
| 123 | +- 空间复杂度:**O(N)**, 递归过程使用的堆栈空间 |
| 124 | + |
| 125 | + |
| 126 | + |
| 127 | + |
| 128 | + |
0 commit comments