Skip to content

链表翻转类型题:

什么时候使用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;;
}

中级:

92. 反转链表 II

关于这里的理解:

  • 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;
};

高级:

25. k个一组翻转链表

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;
};
本站访客数 人次 本站总访问量