一日一练: 删除链表的倒数第 N 个结点

176 阅读1分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第9天,点击查看活动详情

给你一个链表,删除链表的倒数第 n **个结点,并且返回链表的头结点

1. 计算总长度

将反向转成正向问题。找到链表的总长度,然后 总长度 - n 就是正向要删除的链表节点的位置。

  • 因为有可能删除头节点,所以要注意使用哨兵节点

  • 删除操作:

    • 能找到前一个节点prepre.next = pre.next.next。(本题中使用)

    image.png

    • 如果链表没有提供head,只给了当前节点cur,就无法从头开始找到前一个pre。这种情况可以通过同时修改cur.valcur.next实现:

    cur.val = cur.next.val
    cur.next = cur.next.next
    

image.png

function removeNthFromEnd(head: ListNode | null, n: number): ListNode | null {
    const dummy = new ListNode(-1, head)
    
    let cur = head
    let count = 0
    // 计算总长度
    while(cur) {
        count++
        cur = cur.next
    }
    // 计算正向需要删除的位置
    let forwardNum = count - n
    cur = dummy
    // cur从哨兵节点开始,forwardNum正好可以找到要删除节点的 pre节点
    while(forwardNum--) {
        cur = cur.next
    }
    // 删除节点
    cur.next = cur.next.next
    return dummy.next        
};

2. 双指针

这里也可以使用双指针实现:

  • 1.先设定一个指针,从头开始走n步,找到第n + 1个节点
  • 2.在设置第二个指针,从哨兵节点开始
  • 3.同时移动两个指针:当第一个指针变成null时,第二个指针正好在要删除指针的前一个位置。
  • 4.删除

image.png

代码如下:

function removeNthFromEnd(head: ListNode | null, n: number): ListNode | null {
    const dummy = new ListNode(-1, head)    
    let cur = head
    let count = n
    while(count--) {
        cur = cur.next
    }
    let other = dummy
    while(cur) {
        cur = cur.next
        other = other.next
    }
    other.next = other.next.next
    return dummy.next      
};

复杂度

两种方式:

  • 时间复杂度: 最多扫描两次,O(N)N为链表长度;
  • 空间复杂度: 常量级缓存:O(1)