• 递归算法:如何删除链表的倒数第 N 个结点
  • 发布于 2个月前
  • 339 热度
    0 评论
前言

删除链表倒数第N个结点,首先肯定是要找到结点位置的,所以就看怎么快速找第N个位置。

思路1:假设链表长度为L,那么倒数第N个位置就是正数的第L-N+1个位置,比较简单吧,一个班50个同学,倒数第2名,那就是正数第49名(50-2+1)。

思路2:快慢指针,来两个指针,第一个先走N步,然后第一个和第二个一起开始走,第一个走到末尾了,第二个走到N-1的位置,下一个位置就是N。思路3:使用递归进行删除。


练习:19给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
示例 1:
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

示例 2:
输入:head = [1], n = 1
输出:[]

示例 3:
输入:head = [1,2], n = 1
输出:[1]

动手
递归可以分为两步,一是向前推进,二是反向回归,本题中反向回归就可以完成倒数计数,回归一次就是倒数第一,回归两次就是倒数第二。先来个向前推进,比较简单吧:
public void remove(ListNode head, int n) {
    if (head != null) {
        remove(head.next, n);
    }
}
完善
向前推进了,现在就等在回归的时候,判断第N次回归,第1次就是倒数第1个结点,但是为了删除操作,需要获取删除结点的前一个结点,所以要多回归一次(cur++)再执行删除操作。这里需要单独来一个负责全局计数的变量:
private int cur = 0;
public void remove(ListNode head, int n) {
    if (head != null) {
        remove(head.next, n);
        if (cur++ == n) {
            head.next = head.next.next;
        }
    }
}
细节
基本上就完成了,但还有1个特殊情况,上面cur++相当于多回归了一次才回到删除结点的前一个结点,如果删除的是倒数最后一个结点呢,显然cur++判断的地方无法再向前回归1次了,这时特殊处理下,当cur==n时,说明回归到头了,删除的是头结点,这时pass头结点。
public ListNode removeNthFromEnd(ListNode head, int n) {
    remove(head, n);
    return cur == n ? head.next : head;
}
回顾
在递归回归的时候,开始倒数,顺利拿到了倒数第N-1个结点,有了N-1个结点就可以轻松删除第N个结点了。
用户评论