重排链表

This commit is contained in:
mol
2023-07-31 13:57:28 +08:00
commit ddf756f53d
5 changed files with 220 additions and 0 deletions

48
1.重排链表/复习.js Normal file
View File

@ -0,0 +1,48 @@
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @return {void} Do not return anything, modify head in-place instead.
*/
var reorderList = function (head) {
let slow = head
let fast = head
// 使用快慢指针寻找链表中点
while (fast.next && fast.next.next) {
fast = fast.next.next
slow = slow.next
}
// 拆分成前后链表
let cur = slow.next
slow.next = null
// 反转后链表
let pre = null
while (cur) {
let next = cur.next // 4 null
cur.next = pre // null 3
pre = cur // 3 4
cur = next // 4 null
// null <- 3 <- 4
}
// 合并前后链表
let lp = head
while (lp && pre) {
let ln = lp.next
let rn = pre.next
lp.next = pre
pre.next = ln
lp = ln
pre = rn
}
return head
};

38
1.重排链表/笔记.md Normal file
View File

@ -0,0 +1,38 @@
## 快慢指针
快慢指针中的快慢指的是移动的步长即每次向前移动速度的快慢。例如可以让快指针每次沿链表向前移动2慢指针每次向前移动1次。
### 快慢指针的应用
#### 判断单链表是否为循环链表
让快慢指针从链表头开始遍历,快指针向前移动两个位置,慢指针向前移动一个位置;如果快指针到达NULL说明链表以NULL为结尾不是循环链表。如果 快指针追上慢指针,则表示出现了循环。
```
fast=slow=head;
fast=fast->next->next;
slow=slow->next;
whiletrue{
if (fast==NULL || fast->next==NULL)
return false;
else if (fast==slow || fast->next==slow)
return true;
else{
fast=fast->next->next;
slow=slow->next;
}
}
```
#### 在有序链表中寻找中位数
该方法在不借助计数器变量实现寻找中位数的功能。原理是快指针的移动速度是慢指针移动速度的2倍因此当快指针到达链表尾时慢指针到达中点。程序还要考虑链表结点个数的奇偶数因素当快指针移动x次后到达表尾1+2x说明链表有奇数个结点直接返回慢指针指向的数据即可。如果快指针是倒数第二个结点说明链表结点个数是偶数这时可以根据“规则”返回上中位数或下中位数或上中位数+下中位数)的一半。
```
while (fast&&slow)
{
if (fast->next==NULL)
return slow ->data;
else if (fast->next!= NULL && fast->next->next== NULL)
return (slow ->data + slow ->next->data)/2;
else
{
fast= fast->next;
fast= fast->next;
slow = slow ->next;
}
}
```

42
1.重排链表/解1.js Normal file
View File

@ -0,0 +1,42 @@
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @return {void} Do not return anything, modify head in-place instead.
*/
var reorderList = function (head) {
const node = head
let lastNode = getLastNode(node)
let currentNode = node
while (!(currentNode === lastNode || currentNode.next === lastNode)) {
const secNode = currentNode.next
const lastPrevNode = getPrevNode(node, lastNode)
currentNode.next = lastNode
lastNode.next = secNode
currentNode = secNode
lastNode = lastPrevNode
lastNode.next = null
}
return head
};
function getLastNode(node) {
let lastNode = node;
while (lastNode.next) {
lastNode = lastNode.next
}
return lastNode
}
function getPrevNode(head, node) {
let currentNode = head;
while (currentNode && currentNode.next !== node) {
currentNode = currentNode.next
}
return currentNode
}

37
1.重排链表/题目.md Normal file
View File

@ -0,0 +1,37 @@
给定一个单链表 L 的头节点 head ,单链表 L 表示为:
```
L0 → L1 → … → Ln - 1 → Ln
```
请将其重新排列后变为:
```
L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …
```
不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例 1
![1626420311-PkUiGI-image[1].png](https://picbed.hiiragi.club:8081/i/2023/07/31/64c74c89a9ff8.png)
```
输入head = [1,2,3,4]
输出:[1,4,2,3]
```
示例 2
![1626420320-YUiulT-image[1].png](https://picbed.hiiragi.club:8081/i/2023/07/31/64c74c8a1d9a8.png)
```
输入head = [1,2,3,4,5]
输出:[1,5,2,4,3]
```
提示:
- 链表的长度范围为 `[1, 5 * 104]`
- `1 <= node.val <= 1000`

55
1.重排链表/题解.md Normal file
View File

@ -0,0 +1,55 @@
方法一:快慢指针 + 反转链表 + 合并链表
> 作者lcbin
链接https://leetcode.cn/problems/reorder-list/solution/python3javacgo-yi-ti-yi-jie-kuai-man-zhi-t9u2/
来源力扣LeetCode
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
我们先用快慢指针找到链表的中点,然后将链表的后半部分反转,最后将左右两个链表合并。
```js
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @return {void} Do not return anything, modify head in-place instead.
*/
var reorderList = function (head) {
// 快慢指针找到链表中点
let slow = head;
let fast = head;
while (fast.next && fast.next.next) {
slow = slow.next;
fast = fast.next.next;
}
// cur 指向右半部分链表
let cur = slow.next;
slow.next = null;
// 反转右半部分链表
let pre = null;
while (cur) {
const t = cur.next;
cur.next = pre;
pre = cur;
cur = t;
}
cur = head;
// 此时 cur, pre 分别指向链表左右两半的第一个节点
// 合并
while (pre) {
const t = pre.next;
pre.next = cur.next;
cur.next = pre;
cur = pre.next;
pre = t;
}
};
```
时间复杂度O(n),其中 n 是链表的长度。空间复杂度O(1)