【数据结构与算法】:10道链表经典OJ

news/2024/5/17 22:06:52

目录

  • 1. 移除链表元素
  • 2. 反转链表
    • 2.1反转指针法
    • 2.2 头插法
  • 3. 合并两个有序链表
  • 4. 分隔链表
  • 5. 环形链表
  • 6. 链表的中间节点
  • 7. 链表中倒数第K个节点
  • 8. 相交链表
  • 9. 环形链表的约瑟夫问题
  • 10. 链表的回文结构

1. 移除链表元素

在这里插入图片描述
思路1:遍历原链表,将 val 所在的节点释放掉。(太麻烦)

思路2:创建新链表,再遍历原链表,找到不为 val 的节点尾插到新链表。

![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/342214109d444655a6081115428b345c.png

思路1代码实现如下:

注意:
1.当链表为空时,直接返回NULL即可。
2.当尾插上最后一个有效节点时,此时它的 next 可能还与最后一个节点相链接,一定要断开!


typedef struct ListNode ListNode;
struct ListNode* removeElements(struct ListNode* head, int val) {if (head == NULL)return NULL;//创建一个新链表ListNode* newHead, * newTail;newHead = newTail = NULL;ListNode* pcur = head;//遍历原链表,找不为val的节点尾插while (pcur){ListNode* next = pcur->next;if (pcur->val != val){//没有一个节点if (newHead == NULL){newHead = newTail = pcur;}else{//有一个节点以上newTail->next = pcur;newTail = newTail->next;}}pcur = next;}if (newTail)newTail->next = NULL;return newHead;}

2. 反转链表

在这里插入图片描述

2.1反转指针法

思路:定义三个变量 n1,n2,n3,根据它们的指向关系进行迭代。
在这里插入图片描述

代码实现如下:

注意:
1.当链表为空时,直接返回NULL即可。
2.在迭代过程中别忘记判断 n3 ,防止对空指针解引用。
3.注意循环结束的条件,当 n2 为空时,n1 指向反转后的头,此时循环结束。

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

2.2 头插法

思路:创建一个新链表,遍历原链表,依次取下原链表的每一个节点头插到新链表中。

代码实现如下:

注意:
1.当链表为空时,直接返回NULL即可。
2.头插时可以不用判断没有节点和有节点的情况。


typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head) {if (head == NULL)return NULL;ListNode* newHead, * newTail;newHead = newTail = NULL;ListNode* pcur = head;//一个一个拿下来头插while (pcur){ListNode* next = pcur->next;pcur->next = newHead;newHead = pcur;pcur = next;}return newHead;
}

3. 合并两个有序链表

在这里插入图片描述

思路:创建一个带哨兵位的新链表,遍历两个原链表,比较两个节点的值,哪个小就先尾插到新链表中。

代码实现如下:

注意:
1.当其中一个链表为空时,返回另一个链表即可。
2.创建哨兵位节点,方便尾插。
3.注意循环结束条件,当其中有一个链表走完时,跳出循环。
4.剩下的没走完的那个链表直接链接到后面。不需要用循环链接,因为它们本来就是连在一起的。
5.别忘记释放释放哨兵位节点,释放前要保存下一个节点。


typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {ListNode* n1, * n2;n1 = list1;n2 = list2;if (n1 == NULL)return n2;if (n2 == NULL)return n1;//创建哨兵位节点ListNode* phead = (ListNode*)malloc(sizeof(ListNode));ListNode* newHead, * newTail;newHead = newTail = phead;while (n1 && n2){//比较两个链表中数据的大小,谁小就先尾插到新链表if (n1->val < n2->val){newTail->next = n1;n1 = n1->next;newTail = newTail->next;}else{newTail->next = n2;n2 = n2->next;newTail = newTail->next;}}if (n1)newTail->next = n1;if (n2)newTail->next = n2;ListNode* ret = newHead->next;free(newHead);return ret;}

4. 分隔链表

在这里插入图片描述

思路:创建两个带哨兵位的新链表 less 和 greater 。遍历原链表,把小于x 的节点尾插到 less 链表中,把大于等于x 的节点尾插到greater 链表中。最后把 less 链表中的尾结点的 next 链接到 greater链表中的头结点上。

在这里插入图片描述

代码实现如下:

注意:
1.当链表尾空时,直接返回NULL即可。

2.有可能存在greater 链表中的最后一个结点与原链表中的最后一个结点仍然相链接的情况,一定要断开!


typedef struct ListNode ListNode;
struct ListNode* partition(struct ListNode* head, int x) {if (head == NULL)return NULL;ListNode* lessHead, * lessTail, * greaterHead, * greaterTail;ListNode* pcur = head;//创建哨兵位节点,方便尾插lessHead = lessTail = (ListNode*)malloc(sizeof(ListNode));greaterHead = greaterTail = (ListNode*)malloc(sizeof(ListNode));lessTail->next = NULL;greaterTail->next = NULL;while (pcur){if (pcur->val < x){lessTail->next = pcur;lessTail = lessTail->next;}else{greaterTail->next = pcur;greaterTail = greaterTail->next;}pcur = pcur->next;}lessTail->next = greaterHead->next;greaterTail->next = NULL;return lessHead->next;}

5. 环形链表

在这里插入图片描述

这是一个非常经典的例题,它要用上一种非常经典的算法:快慢指针法

定义一个 slow 变量,fast 变量,从链表的头结点开始,slow 每次走一步,fast 每次走两步,当 slow 进入环中时,fast 开始追逐。若成环,则必会在环内的某处相遇,否则 fast 或是 fast->next 最后会走到NULL。

代码实现如下:

注意:
1.当链表节点个数为偶数个时,若不成环,最终 fast == NULL。
当链表节点个数为奇数个时,若不成环,最终 fast->next == NULL.

2.循环条件 fast && fast->next 的位置不能交换。因为当为偶数个节点,fast走到NULL时,如果是 fast->next && fast ,那就是先执行 fast->next ,对空指针解引用,错误!!


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

6. 链表的中间节点

在这里插入图片描述

也要用快慢指针法。也要分两种情况:
在这里插入图片描述

代码实现如下:

注意:
循环条件 fast && fast->next 的位置不能交换。因为当为偶数个节点,fast走到NULL时,如果是 fast->next && fast ,那就是先执行 fast->next ,对空指针解引用,错误!!


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

7. 链表中倒数第K个节点

在这里插入图片描述

思路:
(1) fast 先走 k 步;
(2) slow 和 fast 再一起走,当 fast == NULL 时,slow 就是倒数第 k 个节点。

代码实现如下:

注意:
当 k 大于链表长度时,fast 会走到 NULL ,不能对空指针解引用,直接返回 NULL。


typedef struct ListNode ListNode;
struct ListNode* FindKthToTail(struct ListNode* pHead, int k ) {ListNode* fast ,*slow;fast = slow = pHead;//fast先走K步while(k--){//K大于链表长度时,直接返回NULLif(fast == NULL){return NULL;}fast = fast->next;}//再两者一起走while(fast){fast = fast->next;slow = slow->next;}return slow;}

8. 相交链表

在这里插入图片描述
首先要明确什么是相交链表:
在这里插入图片描述

思路1:暴力求解,时间复杂度O(N*N)
依次取A链表中的每个节点跟B链表中的所有节点比较。如果有相同地址的节点,则相交,第一次相同地址的节点就是交点,否则就不相交。

思路2:时间复杂度O(N)
(1) 先找到两个链表的尾,同时计算出两个链表的长度;
(2) 求出长度差;
(3) 判断哪个是长链表;
(4) 让长链表先走长度差步;
(5) 最后长短链表一起走,直到找到交点。

思路2代码实现如下:

注意:
要注意步骤(3)中判断长短链表的巧妙方法。可以避免写重复代码。

typedef struct ListNode ListNode;
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {ListNode* TailA,*TailB;TailA = headA;TailB = headB;//找尾同时计算长度int lenA = 0;int lenB = 0;while(TailA->next){TailA = TailA->next;lenA++;}while(TailB->next){TailB = TailB->next;lenB++;}//不相交if(TailA != TailB){return NULL;}//求出长度差int gap = abs(lenA - lenB);//判断哪个是长链表ListNode* longList = headA;//先默认A是长链表ListNode* shortList = headB;if(lenA < lenB){shortList = headA;longList = headB;}//让长链表走长度差步while(gap--){longList = longList->next;}//最后长短链表一起走,找交点while(longList != shortList){longList = longList->next;shortList = shortList->next;}return longList;}

9. 环形链表的约瑟夫问题

在这里插入图片描述

在这里插入图片描述

思路:
首先要创建一个循环链表,定义一个计数器 count 用于数数,再遍历循环链表,当结点的 val == count 时,就"杀死",即销毁该节点。

代码实现如下:

注意:
要学习创建循环链表的方法!

typedef struct ListNode ListNode;//创建节点ListNode* BuyNode(int x){ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));if(newnode == NULL){exit(-1);}newnode->val = x;newnode->next = NULL;return newnode;}//1.创建循环链表ListNode* Createnodecircle(int n){ListNode* phead = BuyNode(1);ListNode* ptail = phead;for(int i=2;i<=n;i++){ptail->next = BuyNode(i);ptail = ptail->next;}//尾连头,成环ptail->next = phead;return ptail;}int ysf(int n, int m ) {ListNode* prev = Createnodecircle(n);ListNode* pcur = prev->next;//开始数数int count = 1;while(pcur->next!=prev->next){if(count == m){prev->next = pcur->next;free(pcur);pcur = prev->next;count = 1;}else{prev = pcur;pcur = pcur->next;count++;}}return pcur->val;}

10. 链表的回文结构

在这里插入图片描述

这个题在牛客网中不能用C语言实现,我们可以选C++,因为C++是兼容C的,在C++中可以使用C的语法。

在这里插入图片描述

思路:
(1) 找到链表的中间节点;
(2) 把中间节点后面的链表进行逆置;
(3) 最后把逆置后的链表的值与中间节点之前的链表的值进行比较,若所有节点相等,则是回文链表,否则不是。有一个链表结束则比较结束。

代码实现如下:

注意:
逆置完之后,中间节点与前一个结点的链接可以不用断开,因为就算链接在一起也不影响判断。若强行断开,徒增麻烦。


//找中间结点
struct ListNode* middleNode(struct ListNode* head) {struct ListNode* slow = head;struct ListNode* 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* n1, * n2, * n3;n1 = NULL, n2 = head, n3 = n2->next;while (n2){n2->next = n1;n1 = n2;n2 = n3;if (n3)n3 = n3->next;}return n1;
}class PalindromeList {public:bool chkPalindrome(ListNode* A) {struct ListNode* mid = middleNode(A);struct ListNode* rHead = reverseList(mid);struct ListNode* curA = A;struct ListNode* curR = rHead;//把逆置后的链表与中间结点之前的链表进行比较while(curA && curR){if(curA->val != curR->val){return false;}else{curA = curA->next;curR = curR->next;}}return true;}};

http://www.mrgr.cn/p/14367321

相关文章

JS:locaton.href=易错点

这个地方我错了两次了,每次还让人找得焦头烂额,或许记下来能让我长点记性 易错写法: 正确写法: 不知道为什么我每次都写成location.href("#"),明明应该是location.href="#",而且因为它并不报错,我这笨脑瓜差错真的很心累

《战神4》和《战神5》有什么联系吗 苹果电脑如何运行《战神4》苹果电脑玩战神 Mac玩游戏 战神5攻略 crossover激活码

《战神4》&#xff08;God of War 2018&#xff09;和《战神5》&#xff08;God of War: Ragnark&#xff09;是一对引人注目的游戏作品&#xff0c;它们不仅在游戏界引起了广泛的关注&#xff0c;也给玩家带来了深入探索北欧神话世界的机会。这两部游戏之间的联系不仅体现在剧…

Codeforces Round 937 (Div. 4) VP记录

距离退役又近了一步!第一次 VP 比赛(也是第一次打 CF)。 感到自己距离退役又近了一步。 A. Stair, Peak, or Neither? 题意 You are given three digits \(a\), \(b\), and \(c\). Determine whether they form a stair, a peak, or neither.A stair satisfies the conditi…

前端使用 Konva 实现可视化设计器(5)

关于第三章提到的 selectingNodesArea,在后续的实现中已经精简掉了。 而 transformer 的 dragBoundFunc 中的逻辑,也直接移动 transformer 的 dragmove 事件中处理。请大家动动小手,给我一个免费的 Star 吧~ 这一章花了比较多的时间调试,创作不易~ github源码 gitee源码 示…

蓝桥杯 每天2题 day6

碎碎念&#xff1a;哇咔咔 要不是中间缺勤一天就圆满day7了&#xff01;最后一晚上&#xff01;写题复习哇咔咔 唉&#xff0c;睡了一觉就看不下去了&#xff0c;&#xff0c;&#xff0c;看看之前的笔记洗洗睡觉&#xff0c;&#xff0c;&#xff0c; 记得打印准考证带好东西…

C语言:约瑟夫环问题详解

前言 哈喽&#xff0c;宝子们&#xff01;本期为大家带来一道C语言循环链表的经典算法题&#xff08;约瑟夫环&#xff09;。 目录 1.什么是约瑟夫环2.解决方案思路3.创建链表头结点4.创建循环链表5.删除链表6.完整代码实现 1.什么是约瑟夫环 据说著名历史学家Josephus有过以下…

CSS aspect-ratio属性设置元素宽高比

aspect-ratio 是CSS的一个属性&#xff0c;用于设置元素的期望宽高比。它设置确保元素保持特定的比例&#xff0c;不受其内容或容器大小的影响。 语法&#xff1a; aspect-ratio: <ratio>;其中 <ratio> 是一个由斜杠&#xff08;/&#xff09;分隔的两个数字&…

在一台恢复测试机器上验证oracle备份有效性

一 目的 定期将生产环境oracle数据库恢复到一台测试环境数据库服务器上&#xff0c;以验证备份是否有效&#xff0c;是否能正常恢复。 二 环境 这里以恢复orcl1库为例&#xff0c;计划在orcl这个实例上进行恢复测试。 三 实验步骤 3.1 在目标端创建和源端一样的备份目录 ①…

玩家——玩家格挡

目的:玩家死亡动画调用 创建护盾蓝图 玩家角色蓝图中编写护盾启用逻辑 创建护盾CDUI 玩家角色蓝图中护盾CD1.玩家死亡动画的调用2.创建护盾蓝图3.玩家角色蓝图中编写护盾启用逻辑4.创建护盾CDUI 5.玩家角色蓝图中实现护盾CD 初始化UI本文来自博客园,作者:荒坂株式会社,博客…

2024年nodejs调用小红书最新关注(粉丝)follow接口,api接口分析2004-04-16

一、打开chrome按f12&#xff0c;点击右上角的“关注”按钮&#xff0c;抓包位置如下&#xff1a; (图1 follow接口) 二、follow接口分析 1、请求地址 https://edith.xiaohongshu.com/api/sns/web/v1/user/follow 2、请求方法: POST 3、请求头&#xff1a; :authority: edith…

吴恩达机器学习-第二课-第二周

吴恩达机器学习 学习视频参考b站:吴恩达机器学习 本文是参照视频学习的随手笔记,便于后续回顾。 TensorFlow实现神经网络模型训练细节 训练步骤与和逻辑回归的比较 训练模型分为三步: 1.确定f(x)函数 2.确定损失函数和代价函数 3.通过数据训练寻找最小值详细介绍 1.创建模型…

分析ARP解析过程

一、实验环境 主机A和主机B连接到交换机&#xff0c;并与一台路由器互连&#xff0c;如图7.17所示&#xff0c;路由器充当网关。 图7.17 二、需求描述 查看 ARP 相关信息,熟悉在PC 和 Cisco 设备上的常用命令,设置主机A和主机B为同一个网段网关设置为路由接口地址。 三、推…

全局视角观看Python备忘录-英文版

全局视角观看Python备忘录-英文版

(三)ffmpeg 解码流程以及函数介绍

一、视频解码流程 二、函数介绍 1.avformat_network_init 函数作用&#xff1a; 执行网络库的全局初始化。这是可选的&#xff0c;不再推荐。 此函数仅用于解决旧GnuTLS或OpenSSL库的线程安全问题。如果libavformat链接到这些库的较新版本&#xff0c;或者不使用它们&#…

关于粉碎名称问题

#include <stdio.h> //?nTest@@3HA 3H 代表返回类型是 int A: 这部分表示函数没有参数。 int nTest = 1000; //?nTest2@@3MA 3M 代表返回类型是 float float nTest2 = 1000; //?nTest3@@3DA 3D代表返回类型是char char nTest3 = 1000; //?nTest4@@3FA 3F代表返回类…

设计模式:时序图

设计模式&#xff1a;时序图 设计模式&#xff1a;时序图时序图元素&#xff08;Sequence Diagram Elements&#xff09;角色&#xff08;Actor&#xff09;对象&#xff08;Object&#xff09;生命线&#xff08;Lifeline&#xff09;控制焦点&#xff08;Focus of Control&am…

i-MES生产制造管理系统-老化时间管控

在生产过程中,产品的可靠性是影响其性能和寿命的关键因素,因此提高产品的可靠性是十分必要的,而老化测试是提高产品可靠性的重要手段之一,老化的时间随着产品不同而变化,因此老化时间管控变得尤为重要! 在MES系统中找到“老化时间”管理的菜单,进进入之后可针对不同的产…

前端准备

完成老师模块的后端了

Unity笔记之下拉刷新列表

这样的效果&#xff1b; 代码&#xff1a; using System; using System.Collections; using System.Collections.Generic; using Sirenix.OdinInspector; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI;public class ScrollRectUpdateView : Mon…

解决禁用启用按钮不管用的问题

这两个不一样导致前端没办法更改状态所以我用了JacksonObjectMapper 使用这个实现JAVA对象转换为json对象public class JacksonObjectMapper extends ObjectMapper {public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";public static final String DEFA…