链表翻转类型题:
什么时候使用while(cur.next)?什么时候使用while(cur)?
使用while(cur):(遍历完之后cur是null)
- 要访问完所有节点,把所有东西都算进去。
- 场景: 完整计数、完整翻转、完整计数。
使用while(cur.next):(遍历完之后cur是最后一个节点)
- 要访问到倒数第二个节点
js
let p0 = dummy;
for(let i=0; i < left-1; i++){
p0 = p0.next; // p0.next 必须存在才能 .next
}- 或者:要安全使用cur.next
js
let cur = head;
while (cur && cur.next) { // 同时用 cur 和 cur.next 保证安全
if (cur.val === cur.next.val) {
// 因为循环条件保证了 cur.next 存在
cur.next = cur.next.next;
} else {
cur = cur.next;
}
}初级:
ts
const reverse = (head: ListNode ) =>{
let pre = null;
let cur = head;
while(cur){
let nxt = cur.next;
cur.next = pre;
pre = cur;
cur = nxt;
}
return pre;;
}中级:
关于这里的理解:
- p0是当前操作节点的前一个节点(所以要通过i<left-1获取)
- pre永远是翻转后的新头节点,也是本次翻转之后的第一个节点。
- 所以,p0要指向pre,所以p0.next=pre。
- 再设计一个group_start_node翻转的起点
- let group_start_node = node_before_reverse.next;
- cur表示当前操作的节点,执行完毕后的位置在本次操作的所有节点的下一个节点,(按下面的那个例子,应该是“5”)
- 最后 group_start_node,被翻转到了最后面,应该把链表接上,所以应该指向"下一部分",也就是cur
ts
/**
* 优化版 - 中级: 92. 反转链表 II
* * 示例: head = [1, 2, 3, 4, 5], left = 2, right = 4
* 目标: [1, 4, 3, 2, 5]
* * 涉及三段链表:
* 1. 不动的部分 (1)
* 2. 待翻转的部分 (2 -> 3 -> 4)
* 3. 翻转后剩下的部分 (5)
*/
function reverseBetween(head: ListNode | null, left: number, right: number): ListNode | null {
let dummy = new ListNode();
dummy.next = head;
let p0 = dummy;
for(let i=0;i<left-1;i++){ // 使用p0找到翻转节点的前一个节点
p0 = p0.next;
}
// 移动一下cur
let cur = p0.next; // cur是翻转节点的第一个节点
let pre = null; // 翻转节点的前一个节点
for(let i=0;i<right-left+1;i++){
let nxt = cur.next
cur.next = pre;
pre = cur;
cur = nxt;
}
// p0指的是翻转节点的前一个节点
// 对照样例,p0.next是2
// 2应该指向5, 此时cur是5,所以 p0.next.next = cur
p0.next = pre;
p0.next.next = cur;
return dummy.next;
};高级:
ts
function reverseKGroup(head: ListNode | null, k: number): ListNode | null {
let dummy = new ListNode();
dummy.next = head;
let cur = head;
let n = 0;
// 获取长度
while(cur){
n++;
cur = cur.next;
}
// 分组,确定次数
let len = Math.floor(n/k);
let p0 = dummy;
for(let i=0;i<len;i++){
let pre = null; // 重置
cur = p0.next; // 每一轮的cur是p0的下一个节点
for(let i=0;i<k;i++){
let nxt = cur.next; // 交换三步走,同上一题
cur.next = pre;
pre = cur;
cur = nxt;
}
let nxt = p0.next; // 本轮交换的第一个节点,是下一次交换的上一个节点,也就是下一次交换的p0;
p0.next.next = cur; // 重置p0的next. 具体指向参考上一题:
p0.next = pre; // 重置cur
p0 = nxt; // 同 let nxt = p0.next;;
}
return dummy.next;
};