数据结构-单链表基础2

news/2025/9/22 12:13:34/文章来源:https://www.cnblogs.com/xujushang/p/19104883

0.基本结构和函数

前置内容,可以访问数据结构-单链表基础1

点击查看代码
typedef int ElemType;
typedef struct LNode {ElemType data;struct LNode *next;
} LNode, *linkList;
void CreateList_R(linkList *L, int n) {*L = (linkList) malloc(sizeof(LNode));(*L)->next = NULL;linkList lastp = *L;for (int i = 0; i < n; i++) {LNode *p = (LNode *) malloc(sizeof(LNode));scanf("%d", &(p->data));p->next = NULL;lastp->next = p;lastp = p;}
}
void PrintList(linkList L) {linkList p = L->next;while (p) {printf("%d ", p->data);p = p->next;}printf("\n");
}

1.单链表的逆置

给定一个单链表,要求空间复杂度O(1)实现逆置

分析
在单链表建立时,利用头插法可以实现逆序输入,原因是每次输入的结点“都挤进了头结点后面的位置”,我们利用这个思路,遍历单链表,每次都对结点使用头插法,就可以实现逆置

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

代码具体实现

点击查看代码
void reverse(linkList *L) {linkList p = (*L)->next;(*L)->next = NULL;while (p) {LNode *q = p->next;p->next = (*L)->next;(*L)->next = p;p = q;}
}

2.单链表的最大值查找

利用单链表表示一个整数序列,在单链表中确定最大值
分析
该问题很简单,一次遍历比较即可

时间复杂度为O(n) 空间复杂度为O(1)

代码具体实现

点击查看代码
int findMax(linkList L) {linkList p = L->next;int max = p->data;while (p){max = max > p->data ? max : p->data;p = p->next;}return max;
}

3.链表的正负分解

给定la表示一个非零整数序列,把链表la分解为两个链表lb和lc,其中lb表的结点为la表中值小于零的结点,而lc表的结点为la表中值大于零的结点。要求空间复杂度为O(1)。
分析
这道题目很简单,遍历链表la后对于正负不同的元素分别使用尾插法插入lb和lc即可

时间复杂度为O(n) 空间复杂度为O(1)

代码具体实现

点击查看代码
void fun(linkList *La,linkList *Lb,linkList *Lc) {*Lb = (linkList) malloc(sizeof(LNode));*Lc = (linkList) malloc(sizeof(LNode));linkList pa = (*La)->next,pb = *Lb,pc = *Lc;while (pa) {linkList temp = pa->next;pa->next = NULL;if (pa->data < 0) {pb->next = pa;pb = pa;} else {pc->next = pa;pc = pa;}pa = temp;}pb->next = NULL;pc->next = NULL;
}

4.单链表的区间删除

给定一个有序链表和最小值和最大值,要求删除该链表中在区间[最小值,最大值]的所有结点,如果一个结点都没删或者全删了认为“删除失败”,不保证最小值最大值合法
分析
首先题目不保证合法,还定义了“删除失败”,所以我们的函数需要返回是否合法且删除成功。在c语言中我们的函数需要使用int类型(或者自定义bool类型)。对于[min,max]区间内的元素删除即可

时间复杂度为O(n) 空间复杂度为O(1)

代码具体实现

点击查看代码
int Delete(linkList *L,int min,int max,int n) {if(min>=max) return 0;linkList p = (*L)->next;linkList lastp = *L;int cnt = 0;while (p) {if(p->data >= min && p->data <= max) {lastp->next = p->next;cnt++;}elselastp = p;p = p->next;}if(cnt == n || cnt == 0) return 0;else return 1;
}

5.两个有序链表的交集

单链表la和lb分别示两个递增的整数集合,求出la和lb的交集并以同样的形式存储,并存放在la中。若无交集,输出0。要求空间复杂度为O(1)
分析
首先题目说了是有序集合,所以没有重复元素,我们使用“快慢指针”法,对于每一个la的结点,值为e,我们使用只需要找到lb中第一个不小于e的结点,比较此结点的值是否为e即可,如果不是就删除,这样做看起来复杂度是O(n^2)的,但是请注意两个链表均有序,所以lb不需要每次从头遍历,故最终时间复杂度为O(max(lena,lenb)),如果遍历结束la的指针还没有结束,需要赋值null代表后面的元素均不符合条件

时间复杂度为O(n) 空间复杂度为O(1)

代码具体实现

点击查看代码
int fun(linkList *La,linkList *Lb) {linkList pa = (*La)->next,pb = (*Lb)->next,pre = *La;pre->next = NULL;int cnt = 0;while(pa){while(pb && pb->data < pa->data)pb = pb->next;//找到第一个不小于此值的结点if(pb && pb->data == pa->data) {cnt ++;pre -> next = pa;pre = pa;//记录上一个符合条件的结点pa = pa->next;pb = pb->next;}else{linkList p = pa;pre -> next = pa -> next;pa = pa -> next;free(p);}}pre -> next = NULL;return cnt;
}

6.两个有序链表的差集

和上一题目类似,所求集合从 la ∩ lb 变成 la - lb
分析
不难发现,这道题目的思考逻辑和上一题完全是相对的,我们在求解两个有序集合的交集时,遇见满足条件(在la中且lb中第一个不小于的元素和其相等)的元素就加入,否则就删除。不难发现 la - lb = la - (la ∩ lb) ,所以我们在上一题的逻辑下,仅需要调换删除和加入的顺序就能实现,即满足条件就删除,不满足条件就加入,此处所说的条件和前文一致。代码实现上需要注意一些小细节,已经在注释中说明

时间复杂度为O(n) 空间复杂度为O(1)

代码具体实现

点击查看代码
int fun(linkList *La,linkList *Lb) {linkList pa = (*La)->next,pb = (*Lb)->next,pre = *La;pre->next = NULL;int cnt = 0;while(pa){while(pb && pb->data < pa->data) pb = pb->next;//读者可以与上一题代码进行比较,仅仅调换了位置if(pb && pb->data == pa->data) {linkList p = pa;pre -> next = pa -> next;pa = pa -> next;free(p);}else{cnt ++;pre -> next = pa;pre = pa;pa = pa->next;//pb = pb->next;/*略微不同的是pb指针不需要后移因为此时pa指向的元素小于pb指向的元素,pa后移后,元素有可能与pb指向的元素相等也有可能仍然小于,所以如果pb后移则会缺少情况*/}}pre -> next = NULL;return cnt;
}

7.单链表的排序

给定单链表,要求给出排序后的单链表

a.冒泡排序

分析
排序,是算法/数据结构的基础,可以说很多高阶数据结构都是基于排序的,对于学习过c语言的读者,必然会冒泡排序算法,这里不再赘述原理,直接给出代码

时间复杂度为O(n^2) 空间复杂度为O(1)

代码具体实现

点击查看代码
void bubble(linkList L) {if (L->next == NULL || L->next->next == NULL) return;linkList p, q, tail = NULL;int isswap;  // 是否发生交换do {isswap = 0;  p = L->next;  // 从第一个节点开始q = p->next;  // 第二个结点开始// 遍历到tail节点while (q != tail) {if (p->data > q->data) {ElemType temp = p->data;p->data = q->data;q->data = temp;isswap = 1;  // 标记发生了交换}// 移动到下一对节点p = q;q = q->next;}// 更新尾指针 下一次不需要处理已排好的尾部tail = p;} while (isswap); 
}

b.归并排序

分析
当然,O(n^2)的排序仅仅能应付校内一些基础的任务,在大部分情况下是无法满足我们的性能需求的,我们需要思考更高效的算法。在上一篇文章中,我介绍了两个有序链表的合并,即归并操作,我们可以利用归并来实现高效的排序。我们需要知道基本基本事实:单个元素一定是有序的 请想象一下,我们比较每两个元素,对其归并,这时候长度为n的链表变成了n/2个有序的长度为2的链表,然后我们再对相邻的长度为2的链表进行归并......以此类推,最终由2个长度为n/2归并,形成了所需要的有序链表。但是我们在代码实现的时候,实现逻辑是反过来的,因为我们不可能先将链表分成n份,然后再分成n/2份。我们反过来利用分治的思想对于排序的算法f(L),我们现把他分成两份,然后分别排序f(L1),f(L2),这样做函数就会递归调用,直到遇到边界情况,即长度为1的情况,此时退出递归。不难发现其时间复杂度由 “分”和“合” 两个阶段共同决定。
1.“分” 阶段:拆分链表
过程:通过快慢指针找到链表的中间节点,将链表拆分为两个子链表(L1和L2),递归处理子链表。
时间开销:每次拆分时,快慢指针遍历半个链表(长度为n/2),时间复杂度为O(n)。
递归结构:对于长度为n的链表,会拆分为两个长度为n/2的子链表,以此类推,直到子链表长度为 1。递归深度为log₂n(二分次数)。
2.“合” 阶段:合并有序链表
过程:Merge函数将两个有序子链表(L1和L2)合并为一个有序链表(L3),通过比较节点值依次链接。
时间开销:合并两个总长度为n的链表时,需要遍历所有节点一次,时间复杂度为O(n)。
递归结构:每层递归的总合并时间为O(n)(所有子链表合并的总长度为n),共log₂n层。
3.总时间复杂度
分阶段总时间:O(n) + O(n/2) + O(n/4) + ... + O(1) = O(n)(等比数列求和)。
合阶段总时间:O(n) × log₂n = O(nlogn)。
整体时间复杂度:O(nlogn)(分阶段时间被合阶段主导)。

时间复杂度为O(nlogn) 空间复杂度为O(logn) 此处空间复杂度为递归调用函数导致

代码具体实现

点击查看代码
void Merge(linkList L1, linkList L2, linkList *L3) {*L3 = (linkList) malloc(sizeof(LNode));(*L3)->next = NULL;linkList p1 = L1->next, p2 = L2->next, lastp = *L3;while (p1 && p2) {if (p1->data < p2->data)lastp->next = p1, p1 = p1->next;elselastp->next = p2, p2 = p2->next;lastp = lastp->next;}lastp->next = (p1) ? p1 : p2;
}void sort(linkList L) {if (L->next == NULL || L->next->next == NULL)	return;linkList L1 = (linkList) malloc(sizeof(LNode)), L2 = (linkList) malloc(sizeof(LNode));L1->next = NULL, L2->next = NULL;linkList slow = L->next, fast = L->next->next; // 快慢指针,找中间值,因为链表不能下标访问data[n/2]while (fast && fast->next) slow = slow->next, fast = fast->next->next;L1->next = L->next, L2->next = slow->next, slow->next = NULL;sort(L1), sort(L2);linkList L3;Merge(L1, L2, &L3);L->next = L3->next;free(L1), free(L2), free(L3);
}

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

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

相关文章

LLM的MCP协议通讯方式详解:Stdio、SSE与流式HTTP的选择与实践 - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

Trellix自动化大规模修复开源漏洞,已修补超6万个项目

Trellix通过自动化工具加速修复Python的tarfile模块中存在15年的路径遍历漏洞(CVE-2007-4559),已成功修补61,895个开源项目,并与GitHub合作实现大规模批量拉取请求的自动化修复流程。Trellix自动化大规模修复开源漏…

AI 编程工具选型速览(2025-09 版) - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

爆款游戏背后:尚娱如何借助阿里云 Kafka Serverless 轻松驾驭“潮汐流量”?

阿里云 Kafka 不仅为尚娱提供了高可靠、低延迟的消息通道,更通过 Serverless 弹性架构实现了资源利用率和成本效益的双重优化,助力尚娱在快速迭代的游戏市场中实现敏捷运营、稳定交付与可持续增长。作者:横槊、永安…

Vben Admin5.0 keepAlive缓存和onActivated未生效

根据文档 https://doc.vben.pro/guide/essentials/route.html#keepalive 需要打开页面缓存,缓存才会生效 在配置文件preferences.ts下开启export const overridesPreferences = defineOverridesPreferences({// overr…

yakit + proxifier 解决`all tls strategy failed报错

yakit + proxifier 解决`all tls strategy failed报错场景 在 proxifier + yakit 时,某一个 https 的 url总会遇到`all tls strategy failed 失败的情况,其余的 url,甚至换成 burp 都 ok,百思不得其解。 解决思路 …

版本速递 | 华为云Versatile智能体平台 新增特性介绍(2025年9月发布)

本文分享自华为云社区《版本速递 | 华为云Versatile智能体平台 新增特性介绍(2025年9月发布)》,作者:Versatile运营小助手。< 华为云Versatile智能体平台 体验入口 >(请在PC端打开) 版本概览 Versatile 92…

详细介绍:传输层TCP 与 安全层SSL/TLS

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

PE程序常见脱壳方案

PE程序常见脱壳方案 基础知识 PE文件格式 PE(Portable Executable)是Windows平台下的可执行文件格式。理解PE结构是成功脱壳的前提,加壳程序的核心策略就是通过篡改、加密或隐藏原始PE头信息来保护代码,这意味着脱…

spring ai基于内存RAG尝鲜

RAG,数据检索增强生成,简单点说你提供一个数据集,让语言模型根据你的数据集回答问题。 1.新增依赖 这次的练习demo是将一个pdf作为数据集,喂给模型做训练生成内存向量库,以此回答问题。下面新增的依赖是pdf阅读和…

基于 IOCP 的协程调度器——零基础深入浅出 C++20 协程

将真实的异步 IO 事件与协程相结合、例子规模控制在 200 行、能编译能运行的 Windows C++20 协程 demo,见过没?今天就给各位老铁整一个,它还支持多协程并发哦~前言 上一篇《基于 epoll 的协程调度器》谈到如何基于 …

Gitee PPM风险矩阵:数字化转型中的项目管理预警雷达

Gitee PPM风险矩阵:数字化转型中的项目管理"预警雷达" 在数字化转型浪潮席卷全球的当下,软件研发项目正面临着前所未有的复杂度和不确定性。根据Gartner最新发布的行业报告显示,2023年全球IT项目的平均延…

同一个灰色,POI取出来却是白色:一次Excel颜色解析的踩坑记录

解析Excel单元格颜色时遇到主题色与普通色差异问题。当单元格使用主题色时,直接获取RGB值会失效,需结合ThemesTable获取基础颜色并考虑tint参数(用于调整明暗度)。通过封装工具类,先判断是否为主题色,再解析基础…

坤驰科技携国产化MTCA解决方案,亮相大科学装置控制系统研讨会

“2025MicroTCA/ATCA在大科学装置控制系统中的应用研讨会”在重庆君豪大饭店召开,北京坤驰科技携国产化MTCA硬件平台及数据采集解决方案参会。国产化 MTCA平台 坤驰科技深耕大科学装置(高能物理、激光、光子光束线等…

找出所有项目引用了哪些 NuGet 包、版本号、对应项目路径,并筛选出“同一个包名但版本不同”的情况。

全局扫描所有 .csproj 文件 打开 PowerShell,运行以下脚本(替换为你的代码根目录): $root = "D:\YourCodeRoot" $results = Get-ChildItem -Path $root -Recurse -Filter *.csproj | ForEach-Object {$p…

人形机器人 —— 电机控制的三种模式 —— 力矩、速度、位置

人形机器人 —— 电机控制的三种模式 —— 力矩、速度、位置电机控制的三种模式: 力矩、速度、位置其实,这三种模式说的并不是很清晰,准确来说应该是缺少了一个变量,那就是时间变量,准确的来说就是在单位时间内的…

解决Windows更新后WPF代码报TypeLoadException异常的困难

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

PC与基恩士PLC通信的C#实现

1 确定通信方式与协议 PC与基恩士PLC通信通常主要通过以太网进行,有时也会使用串口。关键在于PLC型号和支持的协议,常见的有:MC协议 (MELSEC Communication Protocol):这是三菱PLC的协议,但概念类似,基恩士有其自…

Excel 表格技能

1:添加删除线:Alt + 1 , 调出设置面板,添加

labelme标注后的json文件和原图同步按角度旋转

点击查看代码 import json import os import base64 import numpy as np import cv2 from math import cos, sin, radians import argparsedef rotate_point_opencv_style(point, rotation_matrix):"""…