算法刷题-链表-删除链表的倒数第N个节点

news2025/2/1 11:58:56

删除链表的倒数第N个节点

    • 19.删除链表的倒数第N个节点
    • 思路
    • 其他语言版本

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

力扣题目链接

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

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

示例 1:

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

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

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

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

思路

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

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

分为如下几步:

  • 首先这里我推荐大家使用虚拟头结点,这样方便处理删除实际头结点的逻辑,如果虚拟头结点不清楚,可以看这篇: 链表:听说用虚拟头节点会方便很多?

  • 定义fast指针和slow指针,初始值为虚拟头结点,如图:

  • fast首先走n + 1步 ,为什么是n+1呢,因为只有这样同时移动的时候slow才能指向删除节点的上一个节点(方便做删除操作),如图:

  • fast和slow同时移动,直到fast指向末尾,如题:

  • 删除slow指向的下一个节点,如图:

此时不难写出如下C++代码:

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummyHead = new ListNode(0);
        dummyHead->next = head;
        ListNode* slow = dummyHead;
        ListNode* fast = dummyHead;
        while(n-- && fast != NULL) {
            fast = fast->next;
        }
        fast = fast->next; // fast再提前走一步,因为需要让slow指向删除节点的上一个节点
        while (fast != NULL) {
            fast = fast->next;
            slow = slow->next;
        }
        slow->next = slow->next->next; 
        
        // ListNode *tmp = slow->next;  C++释放内存的逻辑
        // slow->next = tmp->next;
        // delete nth;
        
        return dummyHead->next;
    }
};

其他语言版本

java:

public ListNode removeNthFromEnd(ListNode head, int n){
    ListNode dummyNode = new ListNode(0);
    dummyNode.next = head;

    ListNode fastIndex = dummyNode;
    ListNode slowIndex = dummyNode;

    //只要快慢指针相差 n 个结点即可
    for (int i = 0; i < n  ; i++){
        fastIndex = fastIndex.next;
    }

    while (fastIndex.next != null){
        fastIndex = fastIndex.next;
        slowIndex = slowIndex.next;
    }

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

Python:

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
        head_dummy = ListNode()
        head_dummy.next = head

        slow, fast = head_dummy, head_dummy
        while(n>=0): #fast先往前走n+1步
            fast = fast.next
            n -= 1
        while(fast!=None):
            slow = slow.next
            fast = fast.next
        #fast 走到结尾后,slow的下一个节点为倒数第N个节点
        slow.next = slow.next.next #删除
        return head_dummy.next

Go:

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func removeNthFromEnd(head *ListNode, n int) *ListNode {
    dummyHead := &ListNode{}
    dummyHead.Next = head
    cur := head
    prev := dummyHead
    i := 1
    for cur != nil {
        cur = cur.Next
        if i > n {
            prev = prev.Next
        }
        i++
    }
    prev.Next = prev.Next.Next
    return dummyHead.Next
}

JavaScript:

/**
 * @param {ListNode} head
 * @param {number} n
 * @return {ListNode}
 */
var removeNthFromEnd = function(head, n) {
    let ret = new ListNode(0, head),
        slow = fast = ret;
    while(n--) fast = fast.next;
    while (fast.next !== null) {
        fast = fast.next; 
        slow = slow.next
    };
    slow.next = slow.next.next;
    return ret.next;
};

TypeScript:

版本一(快慢指针法):

function removeNthFromEnd(head: ListNode | null, n: number): ListNode | null {
    let newHead: ListNode | null = new ListNode(0, head);
    //根据leetcode题目的定义可推断这里快慢指针均不需要定义为ListNode | null。
    let slowNode: ListNode = newHead;
    let fastNode: ListNode = newHead;

    while(n--) {
        fastNode = fastNode.next!; //由虚拟头节点前进n个节点时,fastNode.next可推断不为null。
    }
    while(fastNode.next) {  //遍历直至fastNode.next = null, 即尾部节点。 此时slowNode指向倒数第n个节点。
        fastNode = fastNode.next;
        slowNode = slowNode.next!;
    }
    slowNode.next = slowNode.next!.next; //倒数第n个节点可推断其next节点不为空。 
    return newHead.next; 
}

版本二(计算节点总数法):

function removeNthFromEnd(head: ListNode | null, n: number): ListNode | null {
    let curNode: ListNode | null = head;
    let listSize: number = 0;
    while (curNode) {
        curNode = curNode.next;
        listSize++;
    }
    if (listSize === n) {
        head = head.next;
    } else {
        curNode = head;
        for (let i = 0; i < listSize - n - 1; i++) {
            curNode = curNode.next;
        }
        curNode.next = curNode.next.next;
    }
    return head;
};

版本三(递归倒退n法):

function removeNthFromEnd(head: ListNode | null, n: number): ListNode | null {
    let newHead: ListNode | null = new ListNode(0, head);
    let cnt = 0;
    function recur(node) {
        if (node === null) return;
        recur(node.next);
        cnt++;
        if (cnt === n + 1) {
            node.next = node.next.next;
        }
    }
    recur(newHead);
    return newHead.next;
};

Kotlin:

fun removeNthFromEnd(head: ListNode?, n: Int): ListNode? {
    val pre = ListNode(0).apply {
        this.next = head
    }
    var fastNode: ListNode? = pre
    var slowNode: ListNode? = pre
    for (i in 0..n) {
        fastNode = fastNode?.next
    }
    while (fastNode != null) {
        slowNode = slowNode?.next
        fastNode = fastNode.next
    }
    slowNode?.next = slowNode?.next?.next
    return pre.next
}

Swift:

func removeNthFromEnd(_ head: ListNode?, _ n: Int) -> ListNode? {
    if head == nil {
        return nil
    }
    if n == 0 {
        return head
    }
    let dummyHead = ListNode(-1, head)
    var fast: ListNode? = dummyHead
    var slow: ListNode? = dummyHead
    // fast 前移 n
    for _ in 0 ..< n {
        fast = fast?.next
    }
    while fast?.next != nil {
        fast = fast?.next
        slow = slow?.next
    }
    slow?.next = slow?.next?.next
    return dummyHead.next
}

PHP:

function removeNthFromEnd($head, $n) {
    // 设置虚拟头节点
    $dummyHead = new ListNode();
    $dummyHead->next = $head;

    $slow = $fast = $dummyHead;
    while($n-- && $fast != null){
        $fast = $fast->next;
    }
    // fast 再走一步,让 slow 指向删除节点的上一个节点
    $fast = $fast->next; 
    while ($fast != NULL) {
        $fast = $fast->next;
        $slow = $slow->next;
    }
    $slow->next = $slow->next->next;
    return $dummyHead->next;
    }

Scala:

object Solution {
  def removeNthFromEnd(head: ListNode, n: Int): ListNode = {
    val dummy = new ListNode(-1, head) // 定义虚拟头节点
    var fast = head // 快指针从头开始走
    var slow = dummy // 慢指针从虚拟头开始头
    // 因为参数 n 是不可变量,所以不能使用 while(n>0){n-=1}的方式
    for (i <- 0 until n) {
      fast = fast.next
    }
    // 快指针和满指针一起走,直到fast走到null
    while (fast != null) {
      slow = slow.next
      fast = fast.next
    }
    // 删除slow的下一个节点 
    slow.next = slow.next.next
    // 返回虚拟头节点的下一个
    dummy.next
  }
}

Rust:

impl Solution {
    pub fn remove_nth_from_end(head: Option<Box<ListNode>>, mut n: i32) -> Option<Box<ListNode>> {
        let mut dummy_head = Box::new(ListNode::new(0));
        dummy_head.next = head;
        let mut fast = &dummy_head.clone();
        let mut slow = &mut dummy_head;
        while n > 0 {
            fast = fast.next.as_ref().unwrap();
            n -= 1;
        }
        while fast.next.is_some() {
            fast = fast.next.as_ref().unwrap();
            slow = slow.next.as_mut().unwrap();
        }
        slow.next = slow.next.as_mut().unwrap().next.take();
        dummy_head.next
    }
}

C语言

/**c语言单链表的定义
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* removeNthFromEnd(struct ListNode* head, int n) {
    //定义虚拟头节点dummy 并初始化使其指向head
    struct ListNode* dummy = malloc(sizeof(struct ListNode));
    dummy->val = 0;
    dummy->next = head;
    //定义 fast slow 双指针
    struct ListNode* fast = head;
    struct ListNode* slow = dummy;

    for (int i = 0; i < n; ++i) {
        fast = fast->next;
    }
    while (fast) {
        fast = fast->next;
        slow = slow->next;
    }
    slow->next = slow->next->next;//删除倒数第n个节点
    head = dummy->next;
    free(dummy);//删除虚拟节点dummy
    return head;
}



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/631370.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微服务_fegin

Feign服务调用 是客户端组件 ruoyi系统中Log\Auth\User用了远程服务调用&#xff0c;用工厂模式给他的报错加了层工厂类&#xff0c;return错误的时候重写了以下方法。 在ruoyi-common-core模块中引入依赖 <!-- SpringCloud Openfeign --><dependency><group…

springboot不香吗?为什么还要使用springcloud--各个组件基本介绍(Feign,Hystrix,ZUUL)

1.Feign负载均衡简介 1.1 Feign是什么 Feign是一个声明式WebService客户端。使用Feign能让编写Web Service客户端更加简单, 它的使用方法是定义一个接口&#xff0c;然后在上面添加注解&#xff0c;同时也支持JAX-RS标准的注解。Feign也支持可拔插式的编码器和解码器。Spring…

07-根据Hutool工具的JWT实现单点登录功能

1、两种单点登录方案 1.1、使用token + Redis 实现单点登录(一般不用了) 1.2、使用JWT实现单点登录 2、 JWT单点登录的原理和JWT存在的问题及解决方案讲解 2.1、JWT结构 > Header 头部信息,主要声明了JWT的签名算法等信息。 > Payload 载荷信息,主要承载了各种声明并…

闲置APP小程序开发 你不喜欢的可能正是别人需要的

生活中我们常常会产生各种闲置物品&#xff0c;尤其是对于有宝宝的家庭来说&#xff0c;孩子小的时候可能会添置各种玩具、婴儿车或者是别的用品&#xff0c;随着孩子渐渐长大&#xff0c;这些东西都用不上了&#xff0c;但是扔了又觉得很可惜&#xff0c;留着又占地方&#xf…

给编程初学者写一篇简单的文章,快速查询本机ip,黑别人电脑的基基础

给编程小白写一篇简单的文章&#xff0c;快速查询本机ip 首先&#xff0c;按下win徽标键r键 &#xff08;先按win键再按r键&#xff09; 出现一个小窗口 在里面输入cmd 然后&#xff0c;你将得到一个cmd窗口 在里面输入ipconfig&#xff0c;然后按回车 就可以得到ip地址了 如…

【浅学 MyBatis 】

MyBatis 笔记记录 一、MyBatis基础1. MyBatis介绍及快速入门2. 相关API介绍2.1 Resources2.2 SqlSessionFactory&&SqlSessionFactoryBuilder2.3 SqlSession 3. 映射配置文件4. 核心配置文件4.1 规范写法4.2 参数和返回类型_起别名 5. 引入Log4j 二、MyBatis进阶1. 接口…

Mac环境Royal TSX 从入门使用代替X sheet

Royal TSX 是一款 macOS 下可用的远程连接软件&#xff0c;类似于 Windows 系统的 XShell 。免费版最多支持 10 个连接&#xff0c;对于个人开发而言&#xff0c;已经足够了 一、下载安装 Royal TS 官网&#xff1a;https://www.royalapps.com/ts/win/download 选择 Royal T…

Python Ploty学习: 最简单的Dash App

1 Ploty简介 示例来自官网A Minimal Dash App | Dash for Python Documentation | Plotly有改动 Ploty与matplotlib和seaborn相比&#xff0c;其核心优势在于可交互性&#xff0c;matplotlib和seaborn默认情况下不可交互&#xff0c;具备可交互性的图表在数据分析、展示方面更…

CLIP原理解读——大模型论文阅读笔记一

CLIP原理解读 一. 核心思想 通过自然语言处理来的一些监督信号&#xff0c;可以去训练一个迁移效果很好的视觉模型。 论文的作者团队收集了一个超级大的图像文本配对的数据集&#xff0c;有400 million个图片文本的配对&#xff0c; 模型最大用了ViT-large&#xff0c;提出了…

【JavaEE】使Cookie与Session失效-Servlet上传文件操作-优化表白墙

表白墙退出登录操作-表白墙注册上传头像登录显示头像功能 文章目录 【JavaEE】使Cookie与Session失效-表白墙退出登录操作-Servlet上传文件操作-表白墙注册上传头像登录显示头像功能1. Cookie与Session的删除1.1 表白墙页面增加登录出口1.2 点击链接退出登录1.3 测试 2. 上传文…

【Java线程池详解】—— 每天一点小知识

&#x1f4a7; J a v a 线程池 \color{#FF1493}{Java线程池} Java线程池&#x1f4a7; &#x1f337; 仰望天空&#xff0c;妳我亦是行人.✨ &#x1f984; 个人主页——微风撞见云的博客&#x1f390; &#x1f433; 《数据结构与算法》专栏的文章图文并茂&#x1f…

2023-6-10-第一式工厂方法模式

&#x1f37f;*★,*:.☆(&#xffe3;▽&#xffe3;)/$:*.★* &#x1f37f; &#x1f4a5;&#x1f4a5;&#x1f4a5;欢迎来到&#x1f91e;汤姆&#x1f91e;的csdn博文&#x1f4a5;&#x1f4a5;&#x1f4a5; &#x1f49f;&#x1f49f;喜欢的朋友可以关注一下&#xf…

安卓与串口通信-数据分包的处理

前言 本文是安卓串口通信的第 5 篇文章。本来这篇文章不在计划内&#xff0c;但是最近在项目中遇到了这个问题&#xff0c;正好借此机会写一篇文章&#xff0c;在加深自己理解的同时也让大伙对串口通信时接收数据可能会出现分包的情况有所了解。 其实关于串口通信会可能会出现…

编译tolua——3、以pbc为例子,添加第三方库

目录 1、编译工具和环境说明 2、基础编译tolua 3、以pbc为例子&#xff0c;添加第三方库 4、更新luaJit 大家好&#xff0c;我是阿赵。 之前分享过怎样正常编译基础版本的tolua。这次用添加pbc为例&#xff0c;看看怎样往tolua里面添加其他的第三方库。知道了方法之后&#xf…

【郭东白架构课 模块二:创造价值】31 |节点六: 如何组织阶段性的价值交付?

你好&#xff0c;我是郭东白。上节课我们讲了为什么要做阶段性的价值交付&#xff0c;以及进入阶段性价值交付环节的准备工作。有了这些学习基础&#xff0c;这节课我们就可以进行阶段性价值交付了。 在交付的过程中&#xff0c;主要有三部分工作&#xff1a;目标分解、定义交…

SLAM十四讲——ch4实践(李群李代数)

视觉SLAM14讲----ch4的操作及避坑 一、ch4的实践的准备工作二、各个实践操作1. Sophus的基本使用方法2. 例子&#xff1a;评估轨迹误差 三、遇到的问题 一、ch4的实践的准备工作 确保已经有Sophus库&#xff0c;Sophus库是一个较好的李代数库。 注意&#xff1a; 开始时slamb…

MySQL 数据库实用指南:测试数据准备、SQL语句规范与基本操作

前言 欢迎来到小K的MySQL专栏&#xff0c;本节将为大家准备MySQL测试数据、以及带来SQL语句规范、数据库的基本操作的详细讲解~✨文末送书&#xff0c;小K赠书活动第二期 目录 前言一、准备测试数据二、SQL语句规范三、数据库的基本操作四、总结&#xff1a;文末赠书 一、准备测…

Linux之进程间通信——system V(共享内存、消息队列、信号量等)

文章目录 前言一、共享内存1.共享内存的基本原理2.共享内存的创建3.共享内存的控制参数返回值共享内存的内核数据结构 4.共享内存的关联参数 5.共享内存的去关联6.查看IPC资源7.查看共享内存8.删除共享内存 二、实现进程间通信&#xff08;代码&#xff09;三、共享内存的特点四…

【Newman+Jenkins】实施接口自动化测试

一、是什么Newman Newman就是纽曼手机这个经典牌子&#xff0c;哈哈&#xff0c;开玩笑啦。。。别当真&#xff0c;简单地说Newman就是命令行版的Postman&#xff0c;查看官网地址。 Newman可以使用Postman导出的collection文件直接在命令行运行&#xff0c;把Postman界面化运…

软件测试—冒烟测试

1. 核心 冒烟测试就是完成一个新版本的开发后&#xff0c;对该版本最基本的功能进行测试&#xff0c;保证基本的功能和流程能走通。 如果不通过&#xff0c;则打回开发那边重新开发&#xff1b; 如果通过测试&#xff0c;才会进行下一步的测试(功能测试&#xff0c;集成测试…