链表经典练习题

目录

前言:

一、反转单链表

二、链表的中间结点

三、合并两个有序链表

四、分割链表

五、约瑟夫问题

六、判断链表是否有环?

七、求环形链表的入口点

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

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

十、随机链表的复制

最后:


前言:

        大家经过前面的学习,已经对链表有了初步的了解,那么,我为大家准备了几道题目,大家一起来练习巩固一下吧!

一、反转单链表

        给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

      

 先来一道简单题来练练手,题目的要求为反转链表,相信会有部分同学会说,像数组一样,从后往前遍历不就行了,对此我只能说,很抱歉,这个是单链表,不是双链表(以后做题无说明均为单链表),那既然是单链表,我们又该如何破局呢?大家可自行思考一下方法。

        本题提供的方法为三指针法(方法不唯一,以下题均如此),即运用三指针进行求解。以下为思路:

        代码如下:

typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head) {if(head == NULL){return head;}ListNode* p1,*p2,*p3;p1 = NULL,p2 = head,p3 = p2->next;while(p2){p2->next = p1;p1 = p2;p2 = p3;if(p3)p3 = p3->next;}return p1;
}

         题目链接:. - 力扣(LeetCode)

        大家可自行前去练习。

二、链表的中间结点

给你单链表的头结点 head ,请你找出并返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

         本题方法为:双指针法。

        具体实现思路为:使用快慢指针,一个比另外一个快一步即可。以下是一个例子帮助大家理解:很直观的例子就是五十步笑百步:假设路一共100米,慢逃兵走一步,快逃兵走两步,那么当快逃兵走到终点时,慢逃兵刚好就停在中间,然后开始五十步笑百步。

        代码实现如下:

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

         题目链接:. - 力扣(LeetCode)

三、合并两个有序链表

        将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 

        此题要求为对其合并并排序,相信有人会有这样想法:先插入在排序,其实这个方法说可以也可以,就是有点麻烦,因为这是链表不是数组,修改链表的指向,是比较麻烦的一件事。所以,我们采取以下的方法:创建一个新链表,并设立哨兵位。然后逐渐插入,把这两个链表元素值最小的进行插入,这样即可满足题目要求。 

        代码实现:

typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {//如果List1和list2中有一个为空就直接返回另一个链表if(list1 == NULL){return list2;}if(list2 == NULL){return list1;}//定义l1,l2指针分别指向list1和list2的头节点ListNode* l1, *l2;ListNode* newhead, *newtail;//给新链表的开辟一个哨兵位newhead = newtail = (ListNode*)malloc(sizeof(ListNode));l1 = list1,l2 = list2;while(l1 && l2){if(l1->val <= l2->val){ newtail->next = l1;newtail = newtail->next;l1 = l1->next;}else{newtail->next = l2;newtail = newtail->next;l2 = l2->next;}}if(l1){newtail->next = l1;}if(l2){newtail->next = l2;}//新链表的第一个节点是头节点为无效数据,因此返回头节点的nextreturn newhead->next;
}

         题目链接:. - 力扣(LeetCode)

四、分割链表

给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。

你不需要 保留 每个分区中各节点的初始相对位置。

        对于这道题,题目要求为大于这个值的放在一起,小于的放在一起且不需要保留相对位置。也就是说不用管排好之后的位置,就单纯的分离。现有以下思路:创建两个链表,一个放大的,一个放小的。最后,把大的放到小的后面即可。

typedef struct ListNode ListNode;
struct ListNode* partition(struct ListNode* head, int x){if(head == NULL){return NULL;}ListNode* greathead,*greattail;greathead = greattail = (ListNode*)malloc(sizeof(ListNode));ListNode* lesshead,*lesstail;lesshead =lesstail = (ListNode*)malloc(sizeof(ListNode));ListNode* p = head;while(p){if(p->val >= x){greattail->next = p;greattail = greattail->next;p =p->next;}else{lesstail->next = p;lesstail = lesstail->next;p = p->next;}}greattail->next = NULL;lesstail->next = greathead->next;return lesshead->next;
}

        题目链接:. - 力扣(LeetCode)

五、约瑟夫问题

        著名的Josephus问题 据说著名犹太 Josephus有过以下的故事:在罗⻢⼈占领乔塔帕特后,39 个犹太⼈与 Josephus及他的朋友躲到⼀个洞中,39个犹太⼈决定宁愿死也不要被⼈抓到,于是决定了⼀个⾃杀 ⽅式,41个⼈排成⼀个圆圈,由第1个⼈开始报数,每报数到第3⼈该⼈就必须⾃杀,然后再由下⼀ 个重新报数,直到所有⼈都⾃杀⾝亡为⽌。 历史学家 然⽽Josephus 和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与⾃⼰安排在 第16个与第31个位置,于是逃过了这场死亡游戏。

编号为 1 到 n 的 n 个人围成一圈。从编号为 1 的人开始报数,报到 m 的人离开。

下一个人继续从 1 开始报数。

n-1 轮结束以后,只剩下一个人,问最后留下的这个人编号是多少?

数据范围: 1≤n,m≤10000

进阶:空间复杂度 O(1),时间复杂度 O(n)

         对于,约瑟夫问题大家可能会有过激反应,但是不影响做这道题,大家可参考以下思路:我们先把链表搞成一个环,然后,经行“报数”,一旦复合条件便被free掉。

typedef struct ListNode ListNode ;ListNode* ListBuyNode(int x) {ListNode* node = (ListNode*)malloc(sizeof(ListNode));if (node == NULL) {exit(1);}node->val = x;node->next = NULL;return node;
}
ListNode* CreatCircle(int n) {ListNode* phead = ListBuyNode(1);ListNode* ptail = phead;for (int i = 2; i <= n; i++) {ptail->next = ListBuyNode(i);ptail = ptail->next;}ptail->next = phead;return ptail;
}
int ysf(int n, int m ) {// write code hereListNode* prev = CreatCircle(n);ListNode* pcur = prev->next;int count = 1;while (pcur->next != pcur) {if (count == m) {prev->next = pcur->next;free(pcur);pcur = prev->next;count = 1;} else {prev = pcur;pcur = pcur->next;count++;}}return pcur->val;
}

         题目链接:环形链表的约瑟夫问题_牛客题霸_牛客网

六、判断链表是否有环?

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

        本题可采取如下思路:运用双指针法:分为快慢指针,让它们在链表中遨游,如果能够相遇,则有环,反之,则无环。那么?有没有可能它们相遇不了,答案是:不可能!         代码实现如下:

 typedef struct ListNode ListNode;
bool hasCycle(struct ListNode *head) {ListNode* fast = head;ListNode* slow = head;while(fast && fast->next){fast = fast->next->next;slow = slow->next;if(fast == slow){return true;}}return false;
}

        题目链接:. - 力扣(LeetCode)

七、求环形链表的入口点

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

         

        此问题与上文类似,但明显难度更大,要返回其相遇节点,大家可参考如下写法:其实相遇节点的其余部分和链表长度相同(随后会证明),我们可按照此思路,可使它们相遇,从而求出节点。 

        代码实现如下:

typedef struct ListNode ListNode;
struct ListNode* detectCycle(struct ListNode* head) {ListNode *fast = head, *slow = head;while (fast && fast->next) {fast = fast->next->next;slow = slow->next;if (fast == slow) {ListNode* meet = fast;while (head != meet) {meet = meet->next;head = head->next;}return meet;}}return NULL;
}

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

        实现一种算法,找出单向链表中倒数第 k 个节点。返回该节点的值。

         

        此题过于简单,提供一个思路:首先先定义一个链表前进k步,然后一起前进,结果就为节点值。 

        代码实现如下:

typedef struct ListNode ListNode;int kthToLast(struct ListNode* head, int k){ListNode* p =head;while(k--){p = p->next;}while(p){head = head->next;p = p->next;}return head->val;
}

        题目链接:. - 力扣(LeetCode)

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

        给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

         

        本题可参考如下思路:先对这两个链表进行遍历,得出长度,求出差值,使快链表请进差值步,然后求出相遇点即为公共节点。 代码实现如下:

typedef struct ListNode ListNode;
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {ListNode* p1 = headA;ListNode* p2 = headB;int count1 = 0;int count2 = 0;while(p1){p1 = p1->next;count1++;}while(p2){p2 = p2->next;count2++;}if(p1 != p2){return NULL;}int nums = abs(count1 - count2);ListNode* fast = headB;ListNode* slow = headA;if(count1 > count2){fast = headA;slow = headB;}while(nums--){fast = fast->next;}while(fast != slow){fast = fast->next;slow = slow->next;}return fast;
}

        题目链接:. - 力扣(LeetCode) 

十、随机链表的复制

给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。

构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 

例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 x 和 y ,同样有 x.random --> y 。

返回复制链表的头节点。

                声明:此题极考验链表功底,可以说此题基本上可以检验你单链表学的怎么样,如果看了此题解,能够独立写出来,便可以说掌握了单链表。

        大家可参考以下思路:可以创建一个链表,进行两遍遍历,第一遍先拷贝其的值,第二遍拷贝其指向。

        最后,把链表弄出来就好。

        代码实现:

typedef struct Node Node;
struct Node* copyRandomList(struct Node* head) {Node* p = head;while (p) {Node* copy = (Node*)malloc(sizeof(Node));copy->val = p->val;copy->next = p->next;p->next = copy;p = copy->next;}p = head;while (p) {Node* copy = p->next;if (p->random == NULL) {copy->random = NULL;} else {copy->random = p->random->next;}p = copy->next;}p = head;Node *copyhead = NULL, *copytail = NULL;while (p) {Node* copy = p->next;Node* next = copy->next;if (copyhead == NULL) {copyhead = copytail = copy;} else {copytail->next = copy;copytail = copytail->next;//精华代码}p->next = next;p = next;}return copyhead;
}

         题目链接:. - 力扣(LeetCode)

最后:

        本篇文章到这里就结束了,如果有不理解的题目,可私信或在评论区留下问题,记得一定要练习,这是非常重要的。再强调:大部分题目解法只为了抛砖引玉。希望对大家有所启发。

完!

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

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

相关文章

云原生Kubernetes: K8S 1.29版本 部署Sonarqube

一、实验 1.环境 &#xff08;1&#xff09;主机 表1 主机 主机架构版本IP备注masterK8S master节点1.29.0192.168.204.8 node1K8S node节点1.29.0192.168.204.9node2K8S node节点1.29.0192.168.204.10已部署Kuboard &#xff08;2&#xff09;master节点查看集群 1&…

开箱子咸鱼之王H5游戏源码_内购修复优化_附带APK完美运营无bug最终版__GM总运营后台_附带安卓版本

内容目录 一、详细介绍二、效果展示2.效果图展示 三、学习资料下载 一、详细介绍 1.包括原生打包APK&#xff0c;资源全部APK本地化&#xff0c;基本上不跑服务器宽带 2.优化后端&#xff0c;基本上不再一直跑内存&#xff0c;不炸服响应快&#xff01; 3.优化前端&#xff0c…

WSL2-Ubuntu使用Conda配置百度飞浆paddlepaddle虚拟环境

0x00 缘起 本文将介绍在WSL2-Ubuntu系统中,使用Conda配置百度飞浆paddlepaddle虚拟环境中所出现的各种问题以及解决方法,最终运行"run_check()"通过测试。 在WSL2中配置paddlepaddle不像配置Pytorch那样顺滑,会出现各种问题(如:库的文件缺失、不知道如何匹配C…

Web后端开发中对三层架构解耦之控制反转与依赖注入

内聚与耦合 内聚 比如说我们刚刚书写的员工的实现类 在这里我们仅仅书写的是和员工相关的代码 而与员工无关的代码都没有放到这里 说明内聚程度较高 耦合 以后软件开发要高内聚 低耦合 提高程序灵活性 扩拓展性 分析代码 如何解耦 创建容器 提供一个容器 存储东西 存储E…

STM32 F103C8T6学习笔记16:1.3寸OLED的驱动显示日历

今天尝试使用STM32 F103C8T6驱动显示 1.3寸的OLED&#xff0c;显示数字、字符串、汉字、图片等 本质与0.96寸的OLED是完全相同的原理&#xff1a; 而且经过我的研究发现: 1.3寸大小的OLED并未比0.96寸的有更多的显示像素点数来显示&#xff0c;也是128*64的像素点数显示: 也…

力扣153. 寻找旋转排序数组中的最小值

Problem: 153. 寻找旋转排序数组中的最小值 文章目录 题目描述思路复杂度Code 题目描述 思路 1.初始化左右指针left和right&#xff0c;指向数组的头和尾&#xff1b; 2.开始二分查找&#xff1a; 2.1.定义退出条件&#xff1a;当left right时退出循环&#xff1b; 2.2.当nums…

Python爬虫-BeautifulSoup解析

1.简介 BeautifulSoup 是一个用于解析 HTML 和 XML 文档的 Python 库。它提供了一种灵活且方便的方式来导航、搜索和修改树结构或标记文档。这个库非常适合网页抓取和数据提取任务&#xff0c;因为它允许你以非常直观的方式查询和操作文档内容。 2.安装 Beautiful Soup 终端输…

Time_embedding采样的理解

简单概括&#xff0c;就是t越大&#xff0c;采样得到的点越分散&#xff0c;采样得到的点范围更广 一个简单的示例函数 def time_embedding(t, max_steps1000):frequency np.linspace(0, 1, max_steps)embeddings np.concatenate([np.sin(frequency * t * math.pi),np.cos(f…

【webrtc】RemoteAudioSource的创建线程

m98 代码&#xff1a;I:\webrtc m98_yjf\src\pc\rtp_transmission_manager.cc RtpTransmissionManager::CreateReceiver 在信令线程创建receiver receiver 是&#xff1a; rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>receiver;其实际…

可视化大屏在真实场景的效果,绝对震撼,不要再嘲笑其作用了

hello&#xff0c;我是大千UI工场&#xff0c;本地带来一批可视化大屏现场效果图&#xff0c;非常震撼&#xff0c;给大家带来身临其境的感受&#xff0c;欢迎关注点赞&#xff0c;有需求请私信。 有人可能会认为可视化大屏没有太多价值&#xff0c;可能是因为以下几个原因&am…

记录PR学习查漏补缺

记录PR学习查漏补缺 常用快捷键文件编辑素材序列标记字幕窗口帮助 效果基本3D高斯模糊查找边缘色彩颜色平衡超级键马赛克中间值变形稳定器 常用 快捷键 注意&#xff1a;比较常用的用红色字体显示 文件 快捷键作用Ctrl Alt N新建项目Ctrl O打开项目Ctrl I导入Ctrl S保存…

链表面试题1.

1&#xff0c;反转一个单链表 采用头插法即可 class Solution {public ListNode reverseList(ListNode head) {if(head null){return head;}ListNode cur head.next;head.next null;while(cur ! null){ListNode curN cur.next;cur.next head;head cur ;cur curN;}return …

WEB攻防-PHP特性-piwigoCMS审计实例

前置知识&#xff1a;PHP函数缺陷 测试环境 &#xff1a;piwigo CMS 漏洞URL&#xff1a; 漏洞文件位置&#xff1a;\include \functions_rate.inc.php 漏洞产生入口文件&#xff1a;/picture.php picture.php中接受了一个GET方法action参数&#xff0c;作为switch...case.…

【Java探索之旅】包管理精粹 Java中包的概念与实践

文章目录 &#x1f4d1;前言一、封装1.1 封装的概念1.2 访问限定修饰符 二、封装扩展&#xff08;包&#xff09;2.1 包的概念2.2 带入包中的类2.3 自定义包2.4 常见的包 &#x1f324;️全篇总结 &#x1f4d1;前言 在Java编程中&#xff0c;封装是面向对象编程的核心概念之一…

知识产权 | 守护科技创新之光,共筑知识产权长城

2024年4月26日&#xff0c;迎来了一年一度的世界知识产权日&#xff0c;今年的主题是&#xff1a;“立足创新创造&#xff0c;构建共同未来。” 易我科技是一家专注于数据安全产品研发、生产、销售、服务一体化的高新技术软件企业。易我科技自成立以来&#xff0c;始终秉持尊重…

树莓派4B安装安卓系统LineageOS 21(Android14)

1&#xff1a;系统下载 2&#xff1a;下载好镜像后&#xff0c;准备写入SD卡&#xff0c;我这边使用的是 balenaetcher 3&#xff1a;插入树莓派&#xff0c;按照指示一步一步进行配置&#xff0c;可以配置时区&#xff0c;语言。 注意点 1》:想返回的时候按F2 2》:进入系统…

Redisson分布式锁,重试锁和锁续命的原理

RedissonLock 锁重试原理 tryLock有三个三个参数&#xff0c;第一个是等待时间&#xff0c;第二个是锁失效后自动释放的时间,不填默认为-1&#xff0c;第三个是时间单位&#xff1b; 当设置了第一个参数&#xff0c;那这个锁就成了可重试锁&#xff1b;获取锁失败后&#xff0c…

C++的演变与未来:编程艺术的持续进化

在计算机编程的演变历程中&#xff0c;C以其独特的魅力和强大的功能&#xff0c;一直占据着不可或缺的地位。从最初的面向对象编程&#xff0c;到如今的跨平台、高性能应用&#xff0c;C在不断地适应和推动着计算机技术的发展。本文将深入剖析C的演变过程&#xff0c;展望其未来…

C++ 优先级队列priority_queue

1、常用接口 底层使用的是堆实现&#xff0c;默认为大堆 2、理解&#xff1a; 优先级队列就是即使插入的是一个乱序的&#xff0c;但是依旧会按照有序的顺序出队列 优先级队列就是会根据大小来出队列&#xff0c;而不是谁在队头就出谁。 优先级队列底层是使用堆实现&#x…

Java项目:基于SSM框架实现的实践项目管理系统(ssm+B/S架构+源码+数据库+毕业论文+开题报告)

一、项目简介 本项目是一套基于SSM框架实现的实践项目管理系统&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者。 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff…