LeetCode 👉 HOT 100 👉 删除链表的倒数第 N 个结点 - 中等题

414 阅读3分钟

这是我参与11月更文挑战的第20天,活动详情查看:2021最后一次更文挑战

题目

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

image.png

示例1

输入:head = [1,2,3,4,5], n = 2

输出:[1,2,3,5]

示例2

输入:head = [1], n = 1
输出:[]

思路

在链表的数据结构中,想要删除第n个位置上的节点,需分几种情况

  • 删除的为头节点,只需将头节点的引用指向头节点的字节点,即 head = head.next

  • 删除的为中间节点,需要将第 n - 1 的位置上节点的子节点引用,指向第 n 个节点的子节点,即 Node(n-1).next = Node(n).next

  • 删除的为尾节点,需要将第 n - 1 的位置上节点的子节点引用指向 null

但是需要注意的是,题目要删除的是倒数第 n 个节点,也就是要删除第 len - n 个节点,按照这个思路,可以先遍历一遍计算链表的长度 len,然后遍历链表一次,当达到 len - n 个位置上时,进行删除操作

代码如下

    /**
     * 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 h = head;
        let len = 0;

        // 计算链表长度
        while(h) {
            h = h.next;
            len++;
        }

        // 删除的是头节点
        if(len == n) {
            head = head.next;
            return head;
        }

        h = head;

        // 找到要删除的节点前一个节点
        for(let i = 0; i < len - n - 1; i++) {
            h  = h.next;
        }
        h.next = h.next.next;

        return head;
    };

思考

上面的解法,为了计算链表的长度,完整的遍历了一次链表,在找删除节点的前一个节点时,又遍历了一次链表。有没有一种方法只遍历一次,就找到删除节点的前一个节点呢?

双指针: 可以定义两个指针left, right,初始时,两个指针都是指向头节点;但是先让 right 指针走 n 步,然后再让两个指针同时走,当 right 走到链表的最后时,left 指针指向的就是 len - n 个位置上的节点

但是从上述两次遍历的解法中,我们真正需要找到的是第 len - n - 1 个位置上的节点。此时我们可以在头节点之前在加一个空节点,这样双指针拿到的就是 len - n - 1 个位置上的节点,此时进行删除操作,最后再删除头节点,即为题目所求

代码如下

    /**
     * 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 h = new ListNode(null, head);
        // 定义两个指针
        let left = h, right = h;
        // 右边指针先走
        for(let i = 0; i < n; i++) {
            right = right.next;
        }

        // 当right走到最尾
        while(right.next) {
            right = right.next;
            left = left.next;
        }

        // 删除节点
        left.next = left.next.next;

        // 删除头节点
        h = h.next;

        return h;
    };

小结

上述双指针的算法,也称为快慢指针,比较适用于链表查找倒数第几个节点的问题

LeetCode 👉 HOT 100 👉 删除链表的倒数第 N 个结点 - 中等题

LeetCode 👉 HOT 100 👉 电话号码的字母组合 - 中等题

LeetCode 👉 HOT 100 👉 三数之和 - 中等题

LeetCode 👉 HOT 100 👉 盛最多水的容器 - 中等题

LeetCode 👉 HOT 100 👉 最长回文子串 - 中等题

LeetCode 👉 HOT 100 👉 无重复字符的最长子串 - 中等题

LeetCode 👉 HOT 100 👉 两数相加 - 中等题

LeetCode 👉 HOT 100 👉 两数之和 - 简单题