19. 删除链表的倒数第 N 个结点(剑指 Offer 22. 链表中倒数第k个节点 && 剑指 Offer II 021. 删除链表的倒数第 n 个结点)

题目:

思路:

【1】本质上简单点的就是先扫描一次得到对应的个数,然后第二次扫描的时候,进行删除。

【2】基于上面这种在特殊情况下,又可以变为双指针,因为删除的N是必须不大于链表个数的,所以先一个指针先跑,但是在最差的情况,其实和双循环的效率是一样的,但是最优情况比双循环要少不少次数的扫描。

【3】基于进阶,如果只是针对链表只进行一次扫描,可以考虑扫描后将节点存入辅助中间中再拿出来进行处理。

【4】本质上上面这些其实都是可以算是用到多个循环,但是看怎么理解吧,只用一个循环的话大概率是处理不了的。本身说的一次扫描这个东西的概念就比较模糊。

代码展示:

使用辅助空间完成进阶的要求:

//时间0 ms击败100%
//内存39.5 MB击败66.74%
//时间复杂度:O(L),其中 L 是链表的长度。
//空间复杂度:O(L),其中 L 是链表的长度。主要为栈的开销。
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummy = new ListNode(0, head);
        Deque<ListNode> stack = new LinkedList<ListNode>();
        ListNode cur = dummy;
        while (cur != null) {
            stack.push(cur);
            cur = cur.next;
        }
        for (int i = 0; i < n; ++i) {
            stack.pop();
        }
        ListNode prev = stack.peek();
        prev.next = prev.next.next;
        ListNode ans = dummy.next;
        return ans;
    }
}

 

 双循环的做法:

//时间0 ms击败100%
//内存39.8 MB击败34.10%
//时间复杂度:O(L),其中 L 是链表的长度。
//空间复杂度:O(1)。
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode temp = head;
        int length = 0;
        while (temp != null) {
            length++;
            temp = temp.next;
        }
        ListNode dummy = new ListNode(0, head);
        ListNode cur = dummy;
        for (int i = 1; i < length - n + 1; i++) {
            cur = cur.next;
        }
        cur.next = cur.next.next;
        return dummy.next;
    }
}

 

双指针的做法【双指针其实可以看做是双循环的特殊版本】:

//时间0 ms击败100%
//内存39.3 MB击败96.28%
//时间复杂度:O(L),其中 L 是链表的长度。
//空间复杂度:O(1)。
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummy = new ListNode(0, head);
        ListNode first = head;
        ListNode second = dummy;
        for (int i = 0; i < n; ++i) {
            first = first.next;
        }
        while (first != null) {
            first = first.next;
            second = second.next;
        }
        second.next = second.next.next;
        ListNode ans = dummy.next;
        return ans;
    }
}

 

posted @ 2023-02-06 14:19  忧愁的chafry  阅读(23)  评论(0)    收藏  举报