一网打尽链表的经典OJ题!链表必考笔试题第一弹

news2024/11/25 14:45:20

目录

0.前言

1. 移除链表元素 

2. 反转链表 

2.1 方法一(遍历反转链接关系)

2.2 方法二(节点头插构造新链表)

3.链表的中间节点

4. 链表中倒数第k个节点

5. 总结


0.前言

本文所有代码都已传入gitee,可自取

3链表OJ题p1 · onlookerzy123456qwq/data_structure_practice_primer - 码云 - 开源中国 (gitee.com)https://gitee.com/onlookerzy123456qwq/data_structure_practice_primer/tree/master/3%E9%93%BE%E8%A1%A8OJ%E9%A2%98p1

自我们在上一篇博客结束对单链表的实现之后,我们知道单链表的缺陷是很大的,单纯的单链表的增删查改的意义不大,在现实当中我们也很少使用效率极低的单链表作为主要的数据结构进行存储。可是我们为什么要重视单链表呢?

1. 正是由于单链表的天生缺陷单链表这种数据结构衍生处许多的OJ题许多的笔试题都是有关于单链表的。

2. 单链表虽然单拿出来不能打,但是单链表更多的是去作为更加复杂的数据结构子结构,如哈希桶,邻接表。

本篇博客就开启我们单链表经典OJ刷题的第一弹:

1. 移除链表元素 

203. 移除链表元素 - 力扣(LeetCode)https://leetcode.cn/problems/remove-linked-list-elements/

 题目要求我们把单链表所有为val的节点都删除,之后返回新链表的头结点

 直接进行遍历单链表一遍,进行暴力删除即可:

如果要删除一个节点cur,需要我们free掉cur这个节点,然后紧接着处理链接关系,把cur的前一个节点prv链接到cur的next下一个节点,而cur的next需要在我们free到cur节点之前保存下来,因为释放cur之后我们就不能通过cur节点找到next节点了。

同时cur的prv节点需要我们在上一次迭代的时候对prv进行保存,如果没有进行删除上一次的cur就是这一次的prv;如果上一次进行了删除,那这一次的prv节点仍保持不变,例如我们1->2->6->3,prv为2,cur为6,cur_next为3,也即当我们删除到cur的时候,删除完之后链表变为1->2->3,此时cur往下迭代为3,prv此时仍然是2。

可是处理链接关系的情况仅仅是prv链接到cur_next这么简单吗?我们需要考虑特殊情况,prv事实上是不一定存在的,也即我们删除的节点cur是头结点的时候,此时处理链接关系就不再需要prv链接到cur_next,因为此时prv压根不存在,是NULL,此时就相当于头删,这样处理链接关系就没有压力了,说实话此时就只需更新head即可。

代码如下所示:

struct ListNode* removeElements(struct ListNode* head, int val){
    struct ListNode* cur = head,*prv = NULL;
    //cur一直遍历到空
    while(cur)
    {
        //记录方便下次更新
        struct ListNode* cur_next = cur->next;
        //找到要删除的节点
        if(cur->val == val)
        {
            //要删除的节点是头结点
            if(prv == NULL)
            {
                free(cur);
                //更新head
                head = cur_next;
            }
            else //删除的节点此时是非头节点prv->cur->next
            {
                free(cur);
                prv->next = cur_next;
            }
            //删除之后prv保持不变
        }
        else //该节点不是删除节点 
        {
            prv = cur;
            cur = cur_next;
        }
        //迭代更新
        cur = cur_next;
    }
    return head;
}

2. 反转链表 

剑指 Offer II 024. 反转链表 - 力扣(LeetCode)https://leetcode.cn/problems/UHnkqh/

 这个题有两种方法。

2.1 方法一(遍历反转链接关系)

方法一:可以直接暴力遍历一遍,依次迭代,把prv->cur变成prv<-cur反转链接关系,即可完成反转链表。

 这里需要记住两个点,第一个点是cur和prv的更新,我们下一次反转是把prv变成现在的cur,cur变成cur_next,开始新一轮反转,这需要我们在反转之前记录下cur_next。第二点是记得更新head,因为我们反转之后新的head其实是原链表的tail尾节点

方法一 代码如下:

ListNode* reverseList(ListNode* head) {
        //遍历该链表的所有节点,prv->cur => prv<-cur
        ListNode* prv = nullptr;
        ListNode* cur = head;
        while(cur)
        {
            //记录cur_next
            ListNode* cur_next = cur->next;
            //进行反转
            cur->next = prv;
            //迭代更新
            prv = cur;
            cur = cur_next;
        }
        //原链表最后一个节点为现在的首节点
        return prv;
    }

2.2 方法二(节点头插构造新链表)

我们只需要把原链表的节点从头依次拆卸头插新链表plist中。最后新链表plist呈现的就是反转后的原链表

 不过我们头插就需要注意一个点,那我们头插的方法就是新来的节点作为plist新的head让新头节点链接原有的plist链表实体

ListNode* reverseList(ListNode* head) {
        ListNode* cur = head;
        ListNode* plist = nullptr;
        while(cur)
        {
            ListNode* cur_next = cur->next;
            //cur节点头插到plist
            cur->next = plist;
            plist = cur;
            //迭代遍历下一个节点
            cur = cur_next;
        }
        return plist;
    }

3.链表的中间节点

876. 链表的中间结点 - 力扣(LeetCode)https://leetcode.cn/problems/middle-of-the-linked-list/

单链表我们只能朝一个方向去遍历,寻找链表的中间节点,我们固然可以先遍历一遍计算出单链表的长度len,然后len/2,计算出一半的长度,再遍历从头走len/2步从而找到中间节点

这些写法非常的暴力简单,可是如果我们限制条件,必须只能遍历一遍找到中间节点,上面计算长度的方法就失灵了。这时候我们有新的方法:快慢指针法!!!

我们设计两个指针fast和slow,这两个指针同时从head开始走,但是速度不一样,slow指针就和平常迭代一样,一次走一步slow = slow->nextfast一次走两步fast = fast->next->next。,

然后我们分情况讨论,如果链表有奇数个节点,假设1->2->3->4->5,那中间节点就是3,此时slow和fast同时从1出发fast一次走两步,当fast走了两次到达5,这最后一个节点的时候,此时slow恰走到中间节点3。即奇数个节点时fast->next == NULL的时候停止

如果链表有偶数个节点,假设1->2->3->4->5->6,那中间节点就是3和4,题设要求我们求中间节点中偏后的那个,也就是说我们要寻求的目标是4。slow和fast同时从1出发,fast一次走两步,当fast走了三次,即走到NULL的时候(即6->NULL),slow此时也走到了中间节点4。即偶数个节点时fast == NULL的时候停止

总结一下,slow和fast从head开始,就是一直让fast保持走两步slow保持走一步,无论奇数/偶数个节点,当fast走到NULL / fast->next==NULL的时候停止,此时slow所处的位置就是中间节点。

struct ListNode* middleNode(struct ListNode* head){
    //快慢指针,快指针2步,慢指针1步
    //快指针为NULL / 快指针的next为空结束
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    while(fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
    }
    return slow;
}

4. 链表中倒数第k个节点

剑指 Offer 22. 链表中倒数第k个节点 - 力扣(LeetCode)https://leetcode.cn/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof/

 对于这个题我们仍然是使用快慢指针,一开始快慢指针fast和slow都在head头部先让快指针fast先走k步,然后让快指针fast和慢指针slow在各自位置同时出发,当fast走到NULL的时候,此时slow所处的位置就是倒数第K个节点的位置

然而这个题还是有一个坑的,首先我们自我认识,我们作为接口的设计者,是不是应该考虑到,外部用户在使用的时候,传来的参数不一定都是有效的合法的,也就是说存在这种非法情况:比如我们一个链表只有5个节点,然后传来的k是6,让我们求倒数第6个节点比链表长度还长倒数第6个节点并不存在,是NULL,所以我们必须要考虑这一非法情况。如果在fast走K步迭代的半路上fast提前变成NULL,就是非法情况len<k是这样的,当然如果len == k,这是合法情况fast会在最后一次迭代时变成NULL,所以我们选择在每次迭代之前一步进行检查即可。

struct ListNode* getKthFromEnd(struct ListNode* head, int k){
    //特殊情况
    if(head==NULL)
    return NULL;
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    while(k--)
    {
        //如果k大于链表的长度,则会出现fast为空的情况
        //当然如果k==len。fast也会变为空,不过此时是合法情况,所以我们要在迭代之前检查即可避免错杀这种情况
        if(fast == NULL)
        {
            return NULL;
        }
        fast = fast->next;
    }
    while(fast)
    {
        fast = fast->next;
        slow = slow->next;
    }
    return slow;
}

5. 总结

经典链表OJ题第一弹的内容结束了,还是那句话,看十遍不如码一遍,希望大家看完之后能够亲自写一遍。最后送给大家一个我认为写数据结构相关OJ所需要拥有的重要技能步骤。

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

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

相关文章

使用Electron创建桌面程序,从创建到打包

在桌面程序中&#xff0c;使用C#语言可以创建winform和WPF程序&#xff0c;他们2个在Windows中都非常的优秀&#xff0c;还有就是使用QT开发桌面&#xff0c;可以跨平台开发&#xff0c;这三种都是比较“正规”的&#xff0c;而Electron是使用框架开发桌面程序的&#xff0c;还…

JDBC核心技术_第9章:Apache-DBUtils实现CRUD操作

目录9.1 Apache-DBUtils简介9.2 主要API的使用9.2.1 DbUtils9.2.2 QueryRunner类9.2.3 ResultSetHandler接口及实现类9.1 Apache-DBUtils简介 commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库&#xff0c;它是对JDBC的简单封装&#xff0c;学习成本极低&#xff0…

硬件设备上也能安全运行小程序

当前&#xff0c;在百度、支付宝、今日头条等各大巨头都把持小程序技术尚未开放的情况下&#xff0c;市面上可商用的小程序技术选择面非常狭窄。与此同时&#xff0c;企业仍希望实现 “一次开发&#xff0c;多端运行”&#xff0c;从而真正达到降本增效。今天为大家分享一下&am…

【c++复习梳理】--基础入门语法

目录 1.函数重载 1.1 函数重载概念 1.2C支持函数重载的原理--名字修饰(name Mangling) 2.引用 2.1 引用概念 2.2 引用特性 2.3 常引用 2.4 使用场景 2.4.1 做参数 2.4.2 做返回值 2.5 传值、传引用效率比较 2.5.1 传值、传引用效率比较--做参数返回 2.5.2 值和引用…

Linux--进程地址空间在线程方面的补充--页表的操作模式 0109

上一篇有关地址空间的博客 (入门自用)--Linux--程序地址空间--程序的创建--0907-0913_Gosolo&#xff01;的博客-CSDN博客 很久之前写的&#xff0c;最近会完善。 1. 进程地址空间在线程方面的补充 线程由于资源都是从主进程直接拿来的&#xff0c;所以他们的pcb结构体中的地址…

如何让 Shell 提示符更酷炫

使用远程终端时&#xff0c;默认的命令行提示符格式已经能满足大部分用户需求了&#xff0c;但有时我们希望提示符看起来更直观、优雅、酷炫、美观&#xff0c;可以从中直接得到我们想要的信息&#xff0c;而且清晰分明。本文就详细讲解一下如何让 Shell 提示符更酷炫&#xff…

13_9、Java的IO流之NIO.2中Path、Paths、Files类的使用

一、引入1、Java NIO (New IO&#xff0c;Non-Blocking IO)是从Java 1.4版本开始引入的一套新的IO API&#xff0c;可以替代标准的Java IO API。NIO与原来的IO有同样的作用和目的&#xff0c;但是使用的方式完全不同&#xff0c;NIO支持面向缓冲区的(IO是面向流的)、基于通道的…

cinder对接nfs后端存储

1.部署配置流程 1.安装nfs服务端 可以新增一个节点&#xff0c;或直接使用计算节点&#xff0c;因为存储节点上已经有lvm了这里直接使用计算节点来安装 yum install nfs-utils -y2.修改配置 vi /etc/exports # 要共享的目录 允许的网络1(操做权限) 允许的网络2(操做权限) …

Mybatis源码分析(八)MapperMethod的Select分析

目录一 Select1.1 参数的对应解析convertArgsToSqlCommandParam1.2 ID获取对应的MappedStatement1.3 MappedStatement交给执行器执行1.4 根据参数获取BoundSql1.5 SqlNode节点的解析1.5.1 MixedSqlNode1.5.2 IfSqlNode1.5.3 StaticTextSqlNode1.5.4 TextSqlNode1.6 执行器执行查…

Delete `␍`eslint(prettier/prettier) 错误的解决方案

Delete ‘␍’ eslint(prettier/prettier) 错误的解决方案 问题背景 在Windows笔记本上新拉完代码&#xff0c;在执行pre-commit时&#xff0c;出现如下错误&#xff1a; Delete ␍eslint(prettier/prettier)下面是几种个人尝试过的解决方案&#xff1a; 解决方案 一、Crtl…

【自学Python】Python bytes转string

Python bytes转string Python string转bytes教程 在 Python 中&#xff0c;bytes 类型和 字符串 的所有操作、使用和内置方法也都基本一致。因此&#xff0c;我们也可以实现将 bytes 类型转换成 string 类型。 Python bytes转string方法 Python bytes 转 string 方法主要有…

effective c++读书笔记4

设计class犹如设计type新type的对象应该如何被创建和销毁&#xff1f;&#xff1a;这会影响到你的class的构造函数和析构函数以及内存分配函数和释放函数的设计。对象的初始化和对象的赋值有什么样的区别&#xff1f;这答案决定你的构造函数和赋值操作符的行为&#xff0c;以及…

分享一套Springboot个人博客系统源码带本地搭建教程

Springboot个人博客系统源码带本地搭建教程&#xff0c;需要源码学习可私信我获取。 技术架构 前端框架&#xff1a;JQuery SemanticUI Markdown prism animatecss Tocbot zplayer lightbox 后端框架&#xff1a;SpringBoot 2.2.5 Mybatis Thymeleaf PageHelper m…

基于JAVA SSM springboot实现的抗疫物质信息管理系统设计和实现

基于JAVA SSM springboot实现的抗疫物质信息管理系统设计和实现 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java毕设项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 …

记录一次Spring事务线上异常

Spring事务管理配置方式&#xff1a; XML模糊匹配&#xff0c;绑定事务管理注解&#xff0c;可对每个需要进行事务处理的方法单独配置&#xff0c;只需 Transactional&#xff0c;然后添加属性配置 为简便&#xff0c;本文使用注解方式。Spring初始化时&#xff0c;会通过扫描…

C进阶:通讯录(动态版本 + 文件操作)附源码

本文主要讲解通讯录的代码&#xff1b; 需要拥有结构体&#xff0c;动态内存开辟&#xff0c;文件操作的知识&#xff1b; 目录 &#x1f432;一.通讯录思路 &#x1f54a;️二.三个文件的建立 &#x1f63c;三.所需要使用的变量的创建&#xff08;包含在头文件contact.h中&…

从0到1完成一个Vue后台管理项目(十三、信息列表页面实现:对话框新增、DateTimePicker 日期时间选择器)

往期 从0到1完成一个Vue后台管理项目&#xff08;一、创建项目&#xff09; 从0到1完成一个Vue后台管理项目&#xff08;二、使用element-ui&#xff09; 从0到1完成一个Vue后台管理项目&#xff08;三、使用SCSS/LESS&#xff0c;安装图标库&#xff09; 从0到1完成一个Vu…

JavaScript 事件流

文章目录JavaScript 事件流概述事件冒泡简介onclick() 事件冒泡addEventListener() 事件冒泡stopPropagation() 阻止事件冒泡事件捕获简介addEventListener() 事件捕获W3C标准事件流取消事件默认行为取消使用对象属性绑定的事件的默认行为取消使用addEventListener()绑定的事件…

社科院与杜兰大学金融管理硕士项目你有了解吗?每年招生一期错过申请太可惜了

社科院与杜兰大学金融管理硕士是个什么项目&#xff1f;社科院是所学校吗&#xff0c;怎么都没听说过。杜兰大学又是哪里的学校&#xff1f;前几天有位咨询的同学抛出这些疑问&#xff0c;着实让我不知如何给予回答。像社科院这么低调的院校太少了。社科院全称是中国社会科学院…

错题 5jxn 8253,neg指令,知CPU频率求经典总线周期,如何取一个字,字扩展指令CBW扩展要求,知道相对位移量求转移后指令偏移地址

1&#xff1a;8253工作于方式1时&#xff0c;输出负脉冲的宽度等于() A:计数初值N-1个CLK脉冲宽度 B:计数初值N1个CLK脉冲宽度C:计数初值N个CLK脉冲宽度 D:计数初值(2N-1)/2个CLK脉冲宽度 方式0和方式1 波形相同&#xff08;计数过程中低&#xff0c;计数完高&#xff09;&…