【leetcode】19.删除链表的倒数第N个节点js

题目

代码-"快慢指针"

遍历两趟的就说一下思路吧(因为我没写代码)

第一遍先数一下整个链表有多少个节点,第二遍再找到要删除的节点的前一个位置,然后改变指针指向即可。

只遍历一遍的方法我想到的是"快慢指针"(应该算吧?)。要删除的是倒数第n个节点,那我可以先让一个fast指针走n步,之后slow和fast一起移动,一直到fast走到头了为null了,此时slow就是指向倒数第n个节点了。但这里有个问题,在链表A->B->C中,如果我想删除B,那我应该做的是让A的next指向C,因此我们要找的不是要删除的节点本身,而是要找该节点的前一个节点。所以在slow和fast同步移动之前,还需要让fast多走一步。

javascript 复制代码
/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @param {number} n
 * @return {ListNode}
 */
var removeNthFromEnd = function(head, n) {
    let fast = head, slow = head
    while (n--) {
        fast = fast.next
    }
    // fast为null,说明删除的是头节点
    if (fast === null) return head.next

    fast = fast.next
    while (fast !== null) {
        fast = fast.next
        slow = slow.next
    }
    slow.next = slow.next.next
    return head
};

代码-哑节点

d老师倾情传授。

上面的代码里有对fast的显式判断,但这一步我们可以做优化,让整个代码看起来更简洁。

思路其实还是一样的。前面我们说了要找的是删除节点的前一个节点,因此实际要求fast先走n+1步,这就涉及边界的问题,如果n和链表长度一样,那fast走到null的时候还要往后走一步就会报错。而此时我们在head前面加上一个哑节点就完美解决这个问题。

javascript 复制代码
var removeNthFromEnd = function(head, n) {
    // 创建哑节点,指向原链表头
    let dummy = new ListNode(0, head);
    let fast = dummy;
    let slow = dummy;

    // 快指针先走 n+1 步
    for (let i = 0; i <= n; i++) {
        fast = fast.next;   // fast 不会提前为 null
    }

    // 同步移动,直到 fast 到达末尾
    while (fast !== null) {
        fast = fast.next;
        slow = slow.next;
    }

    // 此时 slow 指向待删除节点的前驱,删除 slow.next
    slow.next = slow.next.next;

    // 返回新链表头(哑节点的下一个)
    return dummy.next;
};

两种方法的时间复杂度都是O(L)(L为链表长度)