LeetCode 19. 删除链表的倒数第 N 个结点

leetcode.cn/problems/re...

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

示例 1:

ini 复制代码
输入: head = [1,2,3,4,5], n = 2
输出: [1,2,3,5]

示例 2:

ini 复制代码
输入: head = [1], n = 1
输出: []

示例 3:

ini 复制代码
输入: head = [1,2], n = 1
输出: [1]

提示:

  • 链表中结点的数目为 sz
  • 1 <= sz <= 30
  • 0 <= Node.val <= 100
  • 1 <= n <= sz

进阶: 你能尝试使用一趟扫描实现吗?

双指针的经典应用,如果要删除倒数第n个节点,让fast移动n步,然后让fast和slow同时移动,直到fast指向链表末尾。删掉slow所指向的节点就可以了。

思路是这样的,但要注意一些细节。

分为如下几步:

  • 首先这里我推荐大家使用虚拟头结点,这样方便处理删除实际头结点的逻辑,如果虚拟头结点不清楚,可以看这篇: 链表:听说用虚拟头节点会方便很多?
  • 定义fast指针和slow指针,初始值为虚拟头结点,如图:
  • fast首先走n + 1步 ,为什么是n+1呢,因为只有这样同时移动的时候slow才能指向删除节点的上一个节点(方便做删除操作),如图:
  • fast和slow同时移动,直到fast指向末尾,如题: //图片中有错别词:应该将"只到"改为"直到"
  • 删除slow指向的下一个节点,如图:
kotlin 复制代码
class Solution {
    fun removeNthFromEnd(head: ListNode?, n: Int): ListNode? {
        //新建一个虚拟头节点指向head
        val dummyNode = ListNode(0)
        dummyNode.next = head
        //快慢指针指向虚拟头节点
        var fastNode: ListNode? = dummyNode
        var slowNode: ListNode? = dummyNode

        // 只要快慢指针相差 n 个结点即可
        for (i in 0..<n) {
            fastNode = fastNode?.next
        }

        while (fastNode?.next != null) {
            fastNode = fastNode.next
            slowNode = slowNode?.next
        }

        // 此时 slowIndex 的位置就是待删除元素的前一个位置。
        // 具体情况可自己画一个链表长度为 3 的图来模拟代码来理解
        slowNode?.next = slowNode.next?.next

        return dummyNode.next
    }
}
  • 时间复杂度: O(n)
  • 空间复杂度: O(1)

递归解法:

java 复制代码
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        // 创建一个新的哑节点,指向原链表头  
        ListNode s = new ListNode(-1, head);
        // 递归调用remove方法,从哑节点开始进行删除操作  
        remove(s, n);
        // 返回新链表的头(去掉可能的哑节点)  
        return s.next;
    }
    
    public int remove(ListNode p, int n) {
        // 递归结束条件:如果当前节点为空,返回0  
        if (p == null) {
            return 0;
        }
        // 递归深入到下一个节点  
        int net = remove(p.next, n);
        // 如果当前节点是倒数第n个节点,进行删除操作  
        if (net == n) {
            p.next = p.next.next;  
        }
        // 返回当前节点的总深度  
        return net + 1;
    }
}
相关推荐
金銀銅鐵1 小时前
费马小定理
python·数学·算法
技术不好的崎鸣同学3 小时前
[ACTF2020 新生赛]Exec 思路及解法
算法·安全·web安全
Full Stack Developme4 小时前
Java LRU 与 LFU 算法及应用
java·开发语言·算法
Jerry5 小时前
LeetCode 707. 设计链表
算法
C语言小火车6 小时前
C++ 堆排序深度精讲:基于完全二叉树的选择排序进化,最坏情况 O(n log n) 的稳定王者
开发语言·c++·算法·排序算法·堆排序
kebidaixu6 小时前
两轮BMS AFE SH367306 I2C 读写时序
算法
智能排队系统_头部供应商6 小时前
RK3588边缘网关改造银行排队系统实战
算法
东方佑6 小时前
临界分词的存在性与最优性:从统计临界态到神经语言模型的双语实证检验
人工智能·算法·语言模型