详解单链表OJ题

链表OJ经典题目

  • 一.删除链表中等于给定值 val 的所有结点
    • leetcode链接
  • 二.给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点
    • leetcode链接
  • 三.反转一个单链表
    • leetcode链接
  • 四.输入一个链表,输出该链表中倒数第k个结点
    • nowcoder链接
  • 五.链表分割
    • nowcoder链接
  • 六.合并两个有序链表
    • leetcode链接
  • 七.判断单链表是否是回文结构
    • nowcoder链接
  • 八 .输入两个链表,找出它们的第一个公共结点
    • leetcode链接
  • 九.给定一个链表,判断链表当中是否有环
    • leetcode链接
  • 十.给定一个链表,返回链表开始入环的第一个结点。 如果链表无环,则返回 NULL
    • leetcode链接
  • 小结

一.删除链表中等于给定值 val 的所有结点

leetcode链接

在这里插入图片描述
方法一:

  • 创建指向当前节点的前一个节点的指针prev=NULL(头节点的前一个节点为空),指针cur指向链表头节点。
  • 以当前节点cur不为空为循环执行条件,判断当前节点的值是否等于val,等于则进行删除,不等于则prev更新为当前节点cur,当前节点cur指向下一个节点。
  • 当前节点的值等于val时:
  1. 如果值为 val 的节点不在头节点(prev 不为 NULL),将 prev 的 next 指针指向当前节点的下一个节点,然后释放当前节点并更新。
  2. 如果值为 val 的节点在链表头节点(prev 为 NULL),则释放原来的头节点,将 head 更新为当前节点的下一个节点。
struct ListNode* removeElements(struct ListNode* head, int val)
{struct ListNode* prev =NULL,*cur=head;while(cur){if(cur->val==val){if(prev){prev->next=cur->next;free(cur);cur=prev->next;}else{cur=cur->next;free(head);head=cur;}}else{prev=cur;cur=cur->next;}}return head;
}

方法二:遍历原链表,不是val的节点尾插到新链表

struct ListNode* removeElements(struct ListNode* head, int val)
{struct LisatNode* newhead=NULL;//创建新链表的头结点struct ListNode* cur=head;struct ListNode* tail=NULL;//创建新链表的尾节点while(cur){if(cur->val==val){struct ListNode* del=cur;//保存要删除的节点cur=cur->next;//移动curfree(del);//将要删除的节点释放,del是局部变量,出了代码块会自动销毁}else    //不是val的值的节点取下来尾插{if(tail==NULL){tail=cur;newhead=tail;}else{tail->next=cur;tail=tail->next;}cur=cur->next;tail->next=NULL;    }}return newhead;
}

二.给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点

leetcode链接

在这里插入图片描述

思路:快慢指针
定义一个慢指针slow一次移动一个节点,一个快指针fast一次移动两个节点,当快指针走完时,返回慢指针,指向的就是中间节点。

  • 当节点数为奇数时,fast移动到尾节点时(也就是fast->next为空的时候),slow指向中间节点
    在这里插入图片描述
  • 当节点数为偶数时,fast移动到NULL时,slow指向中间节点

在这里插入图片描述

  ListNode* middleNode(ListNode* head) {ListNode* slow=head;ListNode* fast=head;while(fast!=NULL&&fast->next!=NULL){slow=slow->next;fast=fast->next->next;}return slow;}

三.反转一个单链表

leetcode链接

在这里插入图片描述

思路:
定义n1指向空,从第一个节点开始,将其next改为前一个节点的地址

  • 注意,如果只定义n1,n2两个指针,当第一个节点的next指向空时,第二个节点的地址就会丢失,所以在这里还需要引入一个指针n3,用于保存下一个节点的地址

在这里插入图片描述

  ListNode* reverseList(ListNode* head) {ListNode* n1=NULL;ListNode* n2=head;if(head==NULL)return NULL;ListNode* n3=head->next;while(n2){n2->next=n1;n1=n2;n2=n3;if(n3)n3=n3->next;}return n1;}
};

思路二:将原链表按照顺序头插到新链表,这样操作完,原链表的顺序就正好反转了

在这里插入图片描述

 ListNode* reverseList(ListNode* head) {ListNode* cur =head;ListNode* newhead=NULL;while(cur){  ListNode* next=cur->next;//保存下一个节点cur->next=newhead;//将cur指向newhead(头插)newhead=cur;//更新新链表地头节点cur=next;//返回原链表保存的节点,重新循环}return newhead;}

四.输入一个链表,输出该链表中倒数第k个结点

nowcoder链接

k为3时,示意图如下
k==3的示意图

分析思路
定义两个指针指向头节点,先让快指针fast走k步,再让两个指针同时走,这样的话,两个指针之间的距离就是k,当快指针指向空的位置时,慢指针就指向了倒数第k的位置

在这里插入图片描述

  • 这样写有问题,看看报错的样例
    在这里插入图片描述
  • 也就是说给定的k可能比链表的节点数大,这种情况下,返回空链表即可,只需要在fast移动k步的时候判断fast是否已经指向NULL即可
struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {struct ListNode* fast =pListHead,*slow=pListHead ;//fast先走k步while(k--){if(fast==NULL)//如果k大于链表节点数,在这个循环中fast就会移动到空,这种情况直接返回NULLreturn NULL;fast = fast->next;  }//同时走while(fast){slow =slow->next;fast=fast->next;}return slow;
}

五.链表分割

nowcoder链接

在这里插入图片描述

创建两个链表,把小于x的值放到链表1,大于x的放到链表2,再把两个链表连接起来

在这里插入图片描述
如果给定的值为1的话,第一个链表为空,需要单独判断单独处理,如果给定的值过大,第二个链表也可能为空。因此带上哨兵位会更简单。
带哨兵位的好处:不管哪个链表为空,这四个指针都不为空
在这里插入图片描述

  ListNode* partition(ListNode* pHead, int x) {struct ListNode*head1,*tail1,*head2,*tail2;head1=tail1=(struct ListNode*)malloc(sizeof(struct ListNode));head2=tail2=(struct ListNode*)malloc(sizeof(struct ListNode));//这里直接开辟出头节点的空间,后续访问next就不用考虑为空的情况,更简单一些struct ListNode* cur =pHead;while(cur){if(cur->val<x){tail1->next=cur;tail1=tail1->next;}else{tail2->next=cur;tail2=tail2->next;          }cur=cur->next;}tail1->next=head2->next;pHead=head1->next;free(head1);free(head2);return pHead;}

上述代码看似没毛病,但是却报错了
在这里插入图片描述
考虑一下这种极端情况,如果刚才的数据后再来个1,这里的tail1和tail2就会形成环,造成死循环
在这里插入图片描述
所以要把tail2->next置空才对,以下是正确代码

 ListNode* partition(ListNode* pHead, int x) {struct ListNode*head1,*tail1,*head2,*tail2;head1=tail1=(struct ListNode*)malloc(sizeof(struct ListNode));head2=tail2=(struct ListNode*)malloc(sizeof(struct ListNode));struct ListNode* cur =pHead;while(cur){if(cur->val<x){tail1->next=cur;tail1=tail1->next;}else{tail2->next=cur;tail2=tail2->next;          }cur=cur->next;}tail1->next=head2->next;tail2->next=NULL;//只改变了这一处pHead=head1->next;free(head1);free(head2);return pHead;}

不得不说,这题是真的坑

六.合并两个有序链表

leetcode链接

在这里插入图片描述
在这里插入图片描述

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{if(list1==NULL)return list2;if(list2==NULL)return list1;//创建合并链表之后的尾节点struct ListNode* tail=NULL;//创建不带哨兵位的头节点struct ListNode* head=NULL;while(list1 && list2){//取小的尾插if(list1->val<list2->val){//由于不带哨兵位,要判断尾节点是不是空if(tail==NULL){tail=head=list1;}else{tail->next=list1;tail=tail->next;}list1=list1->next;}else{if(tail==NULL){tail=head=list2;}else{tail->next=list2;tail=tail->next;}list2=list2->next;} }//其中一个链表走到空跳出循环,把不是空的链表剩余部分链接起来if(list1)tail->next=list1;if(list2)tail->next=list2;return head;
}

七.判断单链表是否是回文结构

nowcoder链接

在这里插入图片描述

思路:
找到中间节点
逆置
比较前半段和后半段,如果相同就是回文序列
注意奇数个节点和偶数个节点的区别
偶数个节点
在这里插入图片描述
奇数个节点
在这里插入图片描述

ListNode* middleNode(ListNode* head) {ListNode* slow=head;ListNode* fast=head;while(fast!=NULL &&fast->next!= NULL){slow=slow->next;fast=fast->next->next;}return slow;}ListNode* reverseList(ListNode* head) {ListNode* cur =head;ListNode* newhead=NULL;while(cur){  ListNode* next=cur->next;//保存下一个节点cur->next=newhead;//将cur指向newhead(头插)newhead=cur;//更新新链表地头节点cur=next;//返回原链表保存的节点,重新循环}return newhead;}bool chkPalindrome(ListNode* head){//调用返回中间节点的函数struct ListNode* mid=middleNode(head);//调用反转链表的函数struct ListNode* rhead=reverseList(head);while(head&&rhead){if(head->val!=rhead->val)return false;head=head->next;rhead=rhead->next;}return true;}

八 .输入两个链表,找出它们的第一个公共结点

leetcode链接


在这里插入图片描述
在这里插入图片描述


  • 先明确下啥叫链表相交

在这里插入图片描述


  • 这样的才符合

在这里插入图片描述


还有一个要注意,节点相交是说节点地址一样,而不是里面的值是相等的

  • 怎样判断是否相交????
  • 怎么找交点???

思路一:
暴力求解
A链表中所有的节点依次取B链表找一遍
最坏情况是没有交点,时间复杂度O(n^2)

思路二:
分别找到A,B链表的尾节点
尾节点的地址如果相同,则相交
尾节点的地址如果不相同,就不相交
分别求出A,B的长度
长的先走差距步,再同时走,第一个相同的就是交点
在这里插入图片描述

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) 
{struct ListNode * cura=headA;struct ListNode *curb=headB;int lena=1;int lenb=1;//找到链表A的尾节点,并且记录链表A节点个数 while(cura->next){lena++;cura=cura->next;}//找到链表B的尾节点,并且记录链表B节点个数 while(curb->next){lenb++;curb=curb->next;}//如果尾节点都不相同,说明没交点,返回NULLif(cura!=curb){return NULL;}//程序走到这里,说明有交点,存好两个链表的节点差值int n=abs(lena-lenb);struct ListNode *longlist=headA,*shortlist=headB;if(lena<lenb){longlist=headB;shortlist=headA;}//让长的先走差距步while(n--){longlist=longlist->next;}//同时走while(longlist!=shortlist){longlist=longlist->next;shortlist=shortlist->next;}//程序走到这里,longlist和shortlist都指向了相交的节点//二者随便返回一个都可以return shortlist;
}

九.给定一个链表,判断链表当中是否有环

leetcode链接

在这里插入图片描述

在这里插入图片描述

思路

  • 给定快慢指针slow,fast
  • 快指针每次走两步,慢指针每次走一步
  • 如果没有环,fast->next->next最终会指向空指针
  • 如果有环,快指针先进环,当慢指针进入环中时,快指针开始追慢指针
  • 每次移动后,快慢指针的距离差值都会缩小1,直到快慢指针相遇

图解
在这里插入图片描述
在这里插入图片描述

  • 代码实现
bool hasCycle(struct ListNode *head) 
{struct ListNode *slow=head,*fast=head;while(fast&&fast->next){slow=slow->next;fast=fast->next->next;if(slow==fast)return true;}return false;
}

思考(纯数学):
慢指针走一步,快指针走三步一定会相遇吗?
在这里插入图片描述
但是第三种情况的条件是不成立的
在这里插入图片描述
用上述推导结果可以看出
在这里插入图片描述

十.给定一个链表,返回链表开始入环的第一个结点。 如果链表无环,则返回 NULL

leetcode链接

在这里插入图片描述

在这里插入图片描述

图解:设出未知量
在这里插入图片描述
从开始到相遇时slow走的距离:L+x
从开始到相遇fast走的距离L+n*C+x
fast路程=slow路程*2
L+n*C+x=2*(L+x)
n*C==L+x
移项得到L=n*C-x
结论:
一个指针从相遇点开始走,一个指针从头开始走,他们会在入口点处相遇

  struct ListNode *detectCycle( struct ListNode *head) {struct ListNode* slow=head,*fast=head;      while(fast && fast->next){slow=slow->next;fast=fast->next->next;if(slow==fast)//相遇了{struct ListNode* meet=slow;while(head!=meet)//基于上述数学推导{meet=meet->next;head=head->next;}return meet;}} //没环return NULL;       }

第二种思路
在这里插入图片描述


小结

  • 链表的题目要考虑周全,访问next之前要想一想程序走到此处有没有可能为空
  • 注意带哨兵位与不带哨兵位的区别
  • 链表一定要多画图

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用 Axios 进行网络请求的全面指南

使用 Axios 进行网络请求的全面指南 本文将向您介绍如何使用 Axios 进行网络请求。通过分步指南和示例代码&#xff0c;您将学习如何使用 Axios 库在前端应用程序中发送 GET、POST、PUT 和 DELETE 请求&#xff0c;并处理响应数据和错误。 准备工作 在开始之前&#xff0c;请…

GPTs的创建与使用,自定义GPTs中的Actions示例用法 定义和执行特定任务的功能模块 通过API与外部系统或服务的交互

Name 等 Logo:自动生成 Name 介绍 Description 介绍 Instructions 要求或命令等 比如用中文回复&#xff0c;角色。 Knowledge 上传你的知识库&#xff0c;如果你有某一垂直行业的数据&#xff0c;基于数据来回答。比如我有某个芯片的指令集。 Capabilities 都要 Actions&…

IO多路转接之select

IO多路转接之select 1. IO多路转接&#xff08;复用&#xff09;2. select2.1 函数原型2.2 细节描述 3. 并发处理3.1 处理流程3.2 通信代码 原文链接 1. IO多路转接&#xff08;复用&#xff09; IO多路转接也称为IO多路复用&#xff0c;它是一种网络通信的手段&#xff08;机…

rk3568 Android12音频问题

rk3568 Android12音频问题 前言 在调试客户的项目的时候&#xff0c;发现外置的音频功放在打开和关闭音频的时候会出现pop音&#xff0c;严重影响使用体验&#xff0c;好在最后解决了这个问题&#xff0c;遂记录一下解决过程。 tiny-alsa命令的使用 在调试ubuntu等一些纯li…

基于深度学习yolov5钢材瑕疵目标检测系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介简介YOLOv5钢材瑕疵目标检测系统特性1. 数据预处理2. 模型架构3. 训练策略4. 后处理 性能评估 二、功能三、系统四. 总结 一项目简介 # YOLOv5 钢材瑕疵目标…

什么是HTML?

✨前言✨ 本文主要介绍什么是HTML以及W3C &#x1f352;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f4dd;私信必回哟&#x1f601; &#x1f352;博主将持续更新学习记录收获&#xff0c;友友们有任何问题可以在评论区留言 文章目录 什么是HTMLHTML发展史HTML的特点什么…

微服务01

笔记&#xff1a; day03-微服务01 - 飞书云文档 (feishu.cn) 数据库连接不上&#xff1f; 要在虚拟机启动MySQL容器。docker start mysql 服务治理 服务提供者&#xff1a;暴露服务接口&#xff0c;供其他服务调用 服务消费者&#xff1a;调用其他服务提供的接口 注册中心&…

销售技巧培训之如何提高手机销售技巧

销售技巧培训之如何提高手机销售技巧 随着科技的迅速发展&#xff0c;手机已成为我们日常生活中不可或缺的一部分。作为一名手机销售员&#xff0c;了解手机销售技巧是必不可少的。本文将通过案例分析与实践&#xff0c;为你揭示手机销售的奥秘。 一、了解客户需求 在销售过程…

二百一十四、Linux——Linux系统时间比电脑时间慢5分钟

一、目的 服务器重启后&#xff0c;发现Linux的系统时间比电脑时间慢5分钟&#xff0c;于是看了些博客&#xff0c;终于找到了解决方法&#xff0c;记录一下&#xff0c;以防止后面出现同样的问题 二、问题 通过date查看&#xff0c;Linux系统时间比电脑时间慢5分钟 &#…

解决firefox(火狐)浏览器使用transform: scale导致的border不显示或显示不全的问题;

最近火狐遇到了此问题&#xff0c;查了许久没有解决办法也有说是因为火狐不支持小于1px单位的&#xff0c;也有说火狐浏览器本身的问题&#xff0c;然后也没有解决方案&#xff0c;最后没办法只能用最笨的方法解决。。。。 只针对Firefox使用CSS&#xff0c;使用’-moz-documen…

vue3移动端脚手架(纯净,集成丰富)

概述 一个纯净的移动端框架 &#xff0c;用到了 Vue3 vuex Vite3 Vant3 sass eslint stylelint htmlhint husky commitlint axios axios-adapter VConsole 自定义全局 loading &#xff0c;自定义函数式 dialog &#xff08;api模仿微信小程序&#xff09;&#x…

Amazon CodeWhisperer 开箱初体验

文章作者&#xff1a;Coder9527 科技的进步日新月异&#xff0c;正当人工智能发展如火如荼的时候&#xff0c;各大厂商在“解放”码农的道路上不断创造出各种 Coding 利器&#xff0c;今天在下就带大家开箱体验一个 Coding 利器&#xff1a; Amazon CodeWhisperer。 亚马逊云科…