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

目录

  • 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.rhkb.cn/news/307365.html

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

相关文章

JavaScript函数式编程

函数式编程 课程介绍 为什么要学习函数编程以及什么是函数式编程函数式编程的特性(纯函数、柯里化、函数组合等)函数式编程的应用场景函数式编程库Lodash 为什么要学习函数式编程 函数式编程是非常古老的一个概念&#xff0c;早于第一台计算机的诞生&#xff0c; 函数式编程…

开源模型应用落地-chatglm3-6b-zero/one/few-shot-入门篇(五)

一、前言 Zero-Shot、One-Shot和Few-Shot是机器学习领域中重要的概念&#xff0c;特别是在自然语言处理和计算机视觉领域。通过Zero-Shot、One-Shot和Few-Shot学习&#xff0c;模型可以更好地处理未知的情况和新任务&#xff0c;减少对大量标注数据的依赖&#xff0c;提高模型的…

心理测评性格测试矩阵版h5微信抖音QQ快手小程序app开源版开发

心理测评性格测试矩阵版h5微信抖音QQ快手小程序app开源版开发 支持SAAS、支持独立加密、支持独立开源、价格不同。 自带题库数据&#xff0c;后台一键初始&#xff0c;支持自己上传题目 心理测评 微信公众号微信小程序抖音小程序可打包APP 支持单题、跳跃题、计分题、因子题、…

OSPF数据报文格式

OSPF协议是跨层封装的协议&#xff0c;跨四层封装&#xff0c;直接将应用层的数据封装在网络层协议后面&#xff0c;IP协议包中协议号字段对应的数值为——89 OSPF的头部信息&#xff1a; ——所有数据包公有的信息 版本&#xff1a;OSPF版本 在IPV4中一般使用OSPFV2&#xf…

第十三届蓝桥杯真题:x进制减法,数组切分,gcd,青蛙过河

目录 x进制减法 数组切分 gcd 青蛙过河 x进制减法 其实就是一道观察规律的题。你发现如果a这个位置上的数x&#xff0c;b这个位置上的数是y&#xff0c;那么此位置至少是max(x,y)1进制。一定要把位置找对啊 #include <bits/stdc.h> using namespace std; typedef l…

easyui combobox下拉框组件输入检索全模糊查询

前引&#xff1a; easyui下拉组件&#xff08;combobox&#xff09;&#xff0c;输入检索下拉内容&#xff0c;是默认的右模糊匹配&#xff0c;而且不支持选择。因业务要求需要做成全模糊查询&#xff0c;目前网上搜索有两种方案&#xff1a; 1.修改easyui源码&#xff0c;这个…

K8S node节点配置

1.开始操作之前要先关闭防火墙&#xff0c;SELinux&#xff0c;swap分区 关闭防火墙 sudo systemctl stop firewalld关闭SELinux sudo setenforce 0 # 临时关闭 sudo sed -i s/^SELINUXenforcing$/SELINUXper…

java快速幂算法

快速幂算法 参考视频(参考五角七边up大佬&#xff09; 幂运算的介绍 幂运算是指将一个数自身乘以自身多次的运算&#xff0c;其表达式为 a n a^n an&#xff0c;其中 a a a 是底数&#xff0c; n n n 是指数。 快速幂解释 快速幂算法是一种用于快速计算幂运算的算法&…

可视化后台管理系统-空框架

1.下载element-plus npm install element-plus --save 注意&#xff1a;element-ui不适配vue3&#xff0c;官方已将vue3版本的更新为element-plus 2.main.js配置 // 全局样式 import ./assets/main.cssimport { createApp } from vue import { createPinia } from piniaimpo…

springboot在使用 Servlet API中提供的javax.servlet.Filter 过滤器 对请求参数 和 响应参数 进行获取并记录日志方案

不多说 直接上代码 第一步 package com.xxx.init.webFilter;import com.alibaba.fastjson.JSONObject; import com.xxx.api.constant.CommonConstant; import com.xxx.api.entities.log.OperationLog; import com.xxx.init.utils.JwtHelper; import com.xxx.init.utils.Reques…

【数据结构】07查找

查找 1. 基本概念2. 顺序表查找2.1 顺序查找2.2 顺序查找优化-哨兵 3. 有序表查找3.1 折半查找&#xff08;二分查找&#xff09; 4. 分块查找&#xff08;索引顺序查找&#xff09;5. Hash表&#xff08;散列表&#xff09;5.1 散列函数的设计5.2 代码实现5.2.1 初始化Hash表5…

个人简历主页搭建系列-06:jqcv 简历主题安装

jqcv 介绍 大家好呀&#xff0c;前段时间我在忙毕设的事情&#xff0c;这段时间继续写这个专题。 我们之前网站已经成功搭建起来了对吧&#xff0c;但是这个样式明显和我们的简历需求不符合&#xff0c;难道我们要自己配置 css 文件一点点进行修改吗&#xff1f; 其实并不用…

无人机概述

1、中英文对照表 中文中文简称英文全称英文简称无人驾驶飞机无人机Unmanned Aerial VehicleUAV无人机自组织网络无人机网络flying Ad-Hoc networkFANET 2、相关概念 2.1鲁棒性 网络鲁棒性是指网络系统在面对随机故障、蓄意攻击或其他异常情况时&#xff0c;能够保持其基本功…

记一次http访问超时服务器端调试

问题&#xff1a;http访问服务器时没有返回&#xff0c;没有超时&#xff0c;一直在阻塞 处理过程&#xff1a;telnet端口能连上&#xff0c;服务端程序也不存在处理时间过长的情况。 说明tcp连接没问题。推测是客户端连接后再发起请求&#xff0c;服务端阻塞了。因为很多客户…

C++:类与对象(三)

目录 再谈构造函数 构造函数体赋值 初始化列表 explicit关键字 static成员 友元 友元函数 友元类 内部类 再次理解封装 再谈构造函数 首先要明白声明、定义、初始化三个概念的不同。 声明&#xff1a;指定变量的名字和类型&#xff0c;可以多次声明。 定义&#xf…

c++ 指针总结

概述 内存地址 在计算机内存中&#xff0c;每个存储单元都有一个唯一的地址(内存编号)。通俗理解&#xff0c;内存就是房间&#xff0c;地址就是门牌号 指针和指针变量 指针&#xff08;Pointer&#xff09;是一种特殊的变量类型&#xff0c;它用于存储内存地址。指针的实质…

【Python】面向对象(专版提升2)

面向对象 1. 概述1.1面向过程1.2 面向对象 2. 类和对象2.1 语法2.1.1 定义类2.1.2 实例化对象 2.2 实例成员2.2.1 实例变量2.2.2 实例方法2.2.3 跨类调用 3. 三大特征3.1 封装3.1.1 数据角度3.1.2 行为角度3.1.3 案例:信息管理系统3.1.3.1 需求3.1.3.2 分析3.1.3.3 设计 3.2 继…

有关格式输入输出的问题

对于格式输入输出问题&#xff0c;我们最好用c语言编写代码&#xff01;&#xff01;&#xff01; 成绩统计 难点&#xff1a;格式化输出 #include <cstdio> using namespace std; typedef long long ll;ll n,score,a,b;int main() {//及格>60 优秀>85 求及格率…

javaEE初阶——多线程(四)

T04BF &#x1f44b;专栏: 算法|JAVA|MySQL|C语言 &#x1faf5; 小比特 大梦想 此篇文章与大家分享多线程专题的第四篇(关于多线程代码案例中的单例模式) 如果有不足的或者错误的请您指出! 目录 九、多线程代码案例(单例模式)1.单例模式1.1饿汉模式1.2懒汉模式1.3使用场景1.4上…

【刷题】图论——最小生成树:Prim、Kruskal【模板】

假设有n个点m条边。 Prim适用于邻接矩阵存的稠密图&#xff0c;时间复杂度是 O ( n 2 ) O(n^2) O(n2)&#xff0c;可用堆优化成 O ( n l o g n ) O(nlogn) O(nlogn)。 Kruskal适用于稀疏图&#xff0c;n个点m条边&#xff0c;时间复杂度是 m l o g ( m ) mlog(m) mlog(m)。 Pr…