数据结构--单链表OJ题

上文回顾---单链表

这章将来做一些链表的相关题目


目录

1.移除链表元素

2.反转链表

3.链表的中间结点

4.链表中的倒数第k个结点

5.合并两个有序链表

6.链表分割

7.链表的回文结构

8.相交链表

9.环形链表

​编辑 

10.环形链表II

​编辑 ​编辑


1.移除链表元素

 

 思路:我们可以通过循环链表,判断结点的val与给出的val是否一致,一致则跳过该结点即可

由于我们是跳过某个结点,那么就会让这个结点的上一个结点和下一个结点进行关联;所以我们以某结点的next的val去判断;所以对于头结点来说,我们还要创建一个结点连接着头结点;

我们自己创建一个结点,还可以规避头指针为空的问题; 

答案:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/struct ListNode* removeElements(struct ListNode* head, int val){//哨兵位struct ListNode* newhead=malloc(sizeof(struct ListNode));newhead->next=head;struct ListNode* cur=newhead;while(cur->next){//判断下一个的值if(cur->next->val==val){cur->next=cur->next->next;}else{cur=cur->next;}}//返回头指针return newhead->next;}

2.反转链表

 

 思路:这道题实际上就是让结点的next改变即可;

所以在这里,我们创建3个指针prev,cur,next,让cur的next指向prev,然后进行指针移动,循环往复;一开始的prev指向NULL;如果head为空,那么就特殊处理,直接返回空;

 

答案:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/struct ListNode* reverseList(struct ListNode* head){//特殊条件if(head==NULL){return NULL;}struct ListNode* cur=head;struct ListNode* prev=NULL;struct ListNode* next=head->next;//移动指针while(cur){cur->next=prev;prev=cur;cur=next;//判断nextif(next!=NULL){next=next->next;}}return prev;
}

3.链表的中间结点

 

 思路1:我们可以先算出链表的总长度,然后再取中间结点的位置

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/struct ListNode* middleNode(struct ListNode* head){//先确定链表长度int i=0;struct ListNode* node=head;while(node!=NULL){node=node->next;i++;}//确定中间节点位置int k=i/2;for(int i=0;i<k;i++){head=head->next;}return head;
}

思路2:利用快慢指针的思想

我们只有在链表遍历到为NULL才知道结束,那么我们是否可以利用当指针到达末尾时就知道中间位置的思想呢?

这里我们用到的快慢指针思想就可以解决该问题,利用它们的速度差,走到的长度就是不一样的;

快指针一次跨越2个结点,而慢指针一次跨越1个结点,等到快指针到达末尾时,慢指针刚好到达中间位置

对于两个中间结点的,我们需要让快指针走到NULL才能达到第二个结点;而一个中间结点的,只需要让快指针走到最后一个结点即可

答案:

struct ListNode* middleNode(struct ListNode* head){//快慢指针struct ListNode* slow,*fast;slow=fast=head;//移动while(fast&&fast->next){slow=slow->next;fast=fast->next->next;}return slow;
}

 


4.链表中的倒数第k个结点

思路:这里还是利用到快慢指针的思想;还是利用快指针到达末尾,然后得知结点的思路;

只不过这里利用的是快慢之间的相对距离,上一题是快慢之间的相对速度;

我们可以让fast指针先走k步,然后在让slow和fast同时走,那么当fast指针到达NULL时, slow刚好到达返回的结点;

 这里要注意的几个特殊情况:头指针为空;k的大小超过链表长度

答案:

/*** struct ListNode {*	int val;*	struct ListNode *next;* };*//*** * @param pListHead ListNode类 * @param k int整型 * @return ListNode类*/
struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {if(pListHead==NULL){return NULL;}//利用相对距离struct ListNode* slow,*fast;slow=fast=pListHead;//先走k步while(k&&fast){fast=fast->next;k--;}if(k>0){return NULL;}while(fast){slow=slow->next;fast=fast->next;}return slow;}

 


5.合并两个有序链表

 

 思路:这里我们先创建一个结点,通过判断两链表的头结点的val大小,来决定连接着哪个

然后循环遍历,移动指针,执行同样的操作;

 当某一链表结束了,需要链接另一条链表剩余的结点;

答案:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){//哨兵位struct ListNode* prehead=malloc(sizeof(struct ListNode));//比大小struct ListNode* prev=prehead;while(list1&&list2){if(list1->val<list2->val){prev->next=list1;list1=list1->next;prev=prev->next;}else{prev->next=list2;list2=list2->next;prev=prev->next;}}//判断哪个链表有剩余prev->next=list1==NULL?list2:list1;return prehead->next;
}

 


6.链表分割

 

 思路:我们可以创建2个结点,第一个结点连接着比x小的结点,第二个连接着>=x的结点

最后将着两个新链表连接在一起;

这里要注意,最后的结点要接空,否则该结点的next保持不变,可能会造成环,将会报错;

 

答案:

/*
struct ListNode {int val;struct ListNode *next;ListNode(int x) : val(x), next(NULL) {}
};*/
class Partition {
public:ListNode* partition(ListNode* pHead, int x) {struct ListNode* prevHead1=(struct ListNode*)malloc(sizeof(ListNode));struct ListNode* prevHead2=(struct ListNode*)malloc(sizeof(ListNode));ListNode* tail1=prevHead1;ListNode* tail2=prevHead2;while(pHead){if(pHead->val<x){tail1->next=pHead;tail1=tail1->next;}else {tail2->next=pHead;tail2=tail2->next;}pHead=pHead->next;}//连接tail1->next=prevHead2->next;//尾置空tail2->next=NULL;return prevHead1->next;}
};

 


7.链表的回文结构

思路:这里我们没有办法从后走向前,单链表是单向的;所以我们可以只能从前往后走;

我们可以先找到中间结点,然后对中间结点之后的链表进行倒转,最后通过中间指针和头指针遍历,判断对应的val是否相同即可

 

 

答案:

/*
struct ListNode {int val;struct ListNode *next;ListNode(int x) : val(x), next(NULL) {}
};*/
class PalindromeList {
public:struct ListNode* middleNode(struct ListNode* head){//快慢指针struct ListNode* slow,*fast;slow=fast=head;//移动while(fast&&fast->next){slow=slow->next;fast=fast->next->next;}return slow;}struct ListNode* reverseList(struct ListNode* head){//特殊条件if(head==NULL){return NULL;}struct ListNode* cur=head;struct ListNode* prev=NULL;struct ListNode* next=head->next;//移动指针while(cur){cur->next=prev;prev=cur;cur=next;//判断nextif(next!=NULL){next=next->next;}}return prev;}bool chkPalindrome(ListNode* head) {ListNode* mid=middleNode(head); //找中间ListNode* rmid=reverseList(mid); //中间位置后面倒置//比较while(rmid){if(head->val!=rmid->val){return false;}head=head->next;rmid=rmid->next;}return true;}
};

 


8.相交链表

 

 

 

 

思路:我们要看作是两条链表,然后后半部分连接是相同部分的结点

发现相交的链表的相交部分都是一样的;我们可以利用它们的尾结点来判断是否相交

对于没有相交的部分,我们无法确保它们的长度;所以我们可以先让比较长的链表先走长度差,然后再一起访问;当访问的结点一致时,表明找到了相交的第一个结点;

答案:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {int lenthA=1,lenthB=1;struct ListNode* curA=headA;struct ListNode* curB=headB;//计算链表长度while(curA->next){++lenthA;curA=curA->next;}while(curB->next){++lenthB;curB=curB->next;}//尾节点判断,不一样说明没有香蕉if(curA!=curB){return NULL;}//长度先走差距步struct ListNode* longList=headA,*shortList=headB;int abs=fabs(lenthA-lenthB);if(lenthA<lenthB){longList=headB;shortList=headA;}while(abs--){longList=longList->next;}//找到相同的交点while(longList!=shortList){longList=longList->next;shortList=shortList->next;}return longList;
}

 这里要注意,curA,curB只能走到尾结点,目的是为了判断它们是否相同,是否相交;


9.环形链表

 

 

 

思路:还是利用快慢指针的思想;

假设有环,通过快慢指针, 快指针一定先进入环中,当慢指针进入环时,让快指针去追逐慢指针

如果快指针走到了空,表示没有环;

这里我们让快指针走两步,让慢指针走1步;当慢指针进入环时,假设快指针到慢指针距离为N,那么没走一次,它们之间的距离就会减1,直至减到N为0,表示快指针追到了

 

答案:

 

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/
bool hasCycle(struct ListNode *head) {//快慢指针//赛道追逐问题struct ListNode* slow=head;struct ListNode* fast=head;while(fast&&fast->next){fast=fast->next->next;slow=slow->next;if(fast==slow){return  true;}}return false;}

这里为什么不让快指针走3步,让慢指针走1步呢?

假设慢指针进入循环后它们之间距离为N

每走一次,它们之间的距离就会减2,N-2,N-4……

如果N为偶数,那么减到最后可以为0,那么就表示追上了,

而如果N为奇数,那么追到最后为1或者-1,不是差一点追上,就是追过了,所以它们的相遇还要考虑环的周长,追过了距离就变为周长-1;

 

到最后,可能追上了,可能一直遇不到;

例如:

 

当slow在左边,fast就在右边;slow在右边,fast就在左边;永远都遇不到

所以为了方便解决问题,就让快指针走2步即可; 


10.环形链表II

 

 

思路:这里我们要在第9题的思想上再引入数学的思想;

假设起点到环入口距离为L,环周长为C,入口到相遇点距离X;

 

fast指针走的距离是slow指针的2倍,所以fast指针会先进入环中, fast可能会在环中绕几圈,我们设为n

那么slow指针走的距离为L+X;

fast指针走的距离为L+n(n>=1)*C+X,fast指针要追上slow,至少要绕一圈环

那么通过它们之间的关系

得到2(L+X)=L+n*C+X

最终得到L=(n-1)*C+C-X;

 也就是说,L的距离会等于n圈环的周长加上相遇点到入口的距离;

那么将得出结论:

一个指针从起点走,一个指针从相遇点走,它们将会在入口点相遇

 答案:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/
struct ListNode *detectCycle(struct ListNode *head) {//快慢指针struct ListNode* fast,*slow;fast=slow=head;//有环while(fast&&fast->next){fast=fast->next->next;slow=slow->next;//相遇点if(fast==slow){struct ListNode* meet=fast;while(meet!=head){meet=meet->next;head=head->next;}return meet;}}return NULL;
}

通过分析之后,我们不需要管那些未知数;

假设L很长,环很小,那么在相遇点的指针就会在环中多走几圈;

假设L很短,那么相遇点到入口点的距离就刚好是L的长度;

所以在写代码中,我们只需要知道起始的指针和相遇点的指针最终会相遇,且就是入口点。

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

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

相关文章

后端进阶之路——浅谈Spring Security用户、角色、权限和访问规则(三)

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 「推荐专栏」&#xff1a; ★java一站式服务 ★ ★前端炫酷代码分享 ★ ★ uniapp-从构建到提升★ ★ 从0到英雄&#xff0c;vue成神之路★ ★ 解决算法&#xff0c;一个专栏就够了★ ★ 架…

项目出bug,找不到bug,如何拉回之前的版本

1.用gitee如何拉取代码 本文为转载于「闪耀太阳a」的原创文章原文链接&#xff1a;https://blog.csdn.net/Gufang617/article/details/119929145 怎么从gitee上拉取代码 1.首先找到gitee上想要拉取得代码URL地址 点击复制这里的https地址 1 ps:&#xff08;另外一种方法&…

Source Insight和Keil中文乱码

1、问题原因 由于Source Insight和Keil中的中文编码方式的不同&#xff0c;导致Keil中添加的中文注释在Source Insight中乱码&#xff1b;在Source Insight中添加的中文注释在Keil中乱码。所以需要统一两者的编码方式。 Source Insight默认编码方式为UTF-8&#xff0c;Keil中一…

C++ | C++11新特性(上)

目录 前言 一、列表初始化 二、声明 1、auto 2、decltype 3、nullptr 三、STL容器的变化 四、右值引用与移动语义 1、左值与左值引用 2、右值与右值引用 3、右值引用与左值引用的比较 4、右值引用的场景及意义 &#xff08;1&#xff09;做参数 &#xff08;2&a…

Java数据流、对象流

文章目录 数据流与对象流说明对象流API认识对象序列化机制如何实现序列化机制反序列化失败问题 数据流与对象流说明 如果需要将内存中定义的变量&#xff08;包括基本数据类型或引用数据类型&#xff09;保存在文件中&#xff0c;那怎么办呢&#xff1f; int age 300; char …

Javascript 数据结构[入门]

作者&#xff1a;20岁爱吃必胜客&#xff08;坤制作人&#xff09;&#xff0c;近十年开发经验, 跨域学习者&#xff0c;目前于海外某世界知名高校就读计算机相关专业。荣誉&#xff1a;阿里云博客专家认证、腾讯开发者社区优质创作者&#xff0c;在CTF省赛校赛多次取得好成绩。…

新手注意事项-visual studio 来实现别踩白块儿

自己之前为了熟悉easyx练习过一个简单的项目&#xff0c;别踩白块儿&#xff0c;链接在这里&#xff0c;别踩白块儿&#xff0c;当时比较稚嫩&#xff0c;很多东西都不会&#xff0c;可以说是只知道最基本的语法&#xff0c;头文件都不知道&#xff0c;一个一个查资料弄懂的&am…

FreeRTOS(vTaskList与vTaskGetRunTimeStats)

目录 1、Cube配置 ①配置SYS ②配置TIM3 ③配置USART2 ④配置FreeRTOS ⑤配置中断优先级 2、代码添加改动 ①在main函数合适位置开启TIM3中断 ②修改HAL_TIM_PeriodElapsedCallback函数 ③完善两个相关函数 ④vTaskList与vTaskGetRunTimeStats的使用 vTaskList&#xff…

c语言每日一练(2)

前言&#xff1a; 每日一练系列&#xff0c;每一期都包含5道选择题&#xff0c;2道编程题&#xff0c;博主会尽可能详细地进行讲解&#xff0c;令初学者也能听的清晰。每日一练系列会持续更新&#xff0c;暑假时三天之内必有一更&#xff0c;到了开学之后&#xff0c;将看学业情…

VUE框架:vue2转vue3全面细节总结(3)路由组件传参

大家好&#xff0c;我是csdn的博主&#xff1a;lqj_本人 这是我的个人博客主页&#xff1a; lqj_本人_python人工智能视觉&#xff08;opencv&#xff09;从入门到实战,前端,微信小程序-CSDN博客 最新的uniapp毕业设计专栏也放在下方了&#xff1a; https://blog.csdn.net/lbcy…

嵌入式开发学习(STC51-18-LCD液晶显示)

内容 在LCD1602液晶上显示字符信息&#xff1b; LCD1602介绍 简介 1602液晶也叫1602字符型液晶&#xff0c;它能显示2行字符信息&#xff0c;每行又能显示16个字符&#xff1b; 它是一种专门用来显示字母、数字、符号的点阵型液晶模块&#xff1b; 它是由若干个5x7或者5x…

防火墙第二次作业

一、什么是防火墙&#xff1f; 百度给出个一个定义&#xff1a;防火墙技术是通过有机结合各类用于安全管理与筛选的软件和硬件设备&#xff0c;帮助计算机网络于其内、外网之间构建一道相对隔绝的保护屏障&#xff0c;以保护用户资料与信息安全性的一种技术。 通俗的来讲&#…