003+limou+C语言链表之“无头单向非循环链表”的实现

news/2024/11/29 10:48:31/

0、前要:顺序表的缺陷

  • 在倍数增容的时候,存储空间存在一定的空间浪费
  • 增容需要申请空间、拷贝数据、释放旧空间,有不小的消耗
  • 头部或者中部的插入、删除效率低下,时间复杂度是O(N)
  • 查找搜索缓慢,但是这个其实是可以靠树来提速的,不过这个和本此讨论的链表没有太大关系

1、链表的基本认知

(1)链表的概念

链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的(逻辑结构是想象出来的/物理结构是是实实在在的,内存中如何存储表示他们之间的关系)

(2)链表的分类

  • 单向、双向
  • 带头、不带头
  • 循环、非循环

尽管在实际中,还是单向和双向链表用的比较多

(3)链表的第一个节点和尾节点

一般叫做plist/phead和pback

(4)链接的本质

将后一个节点的地址存放在前一个节点里

(5)最简单和最复杂的链表结构

  • 无头单向非循环链表的实现(最简单):一般不会直接单独存放数据,实际中更多是作为其他数据结构的子结构,例如哈希桶、图的邻接表等,这种结构在面试的时候也有很多
  • 有头双向循环链表的实现(最复杂):一般直接单独存储数据,实际中使用的链表数据结构,都是带头双向循环链表,虽然结构复杂,但是在一些接口函数实现的时候就会发现反倒是变得简单了

2、无头单向非循环链表的实现(结构最简单)

(1)“无头无循环单向链表”结构体

typedef int SLTDateType;
typedef struct SListNode
{SLTDateType data;struct SListNode* next;
}SListNode;

(2)动态申请一个结点

SListNode* BuySListNode(SLTDateType x)
{//单独申请一个节点SListNode* chche = (SListNode*)malloc(sizeof(SListNode));if (!chche){perror("fail malloc");//注意perror只有在malloc、文件操作的时候才可用return NULL;}else{//初始化该节点chche->data = x;chche->next = NULL;return chche;}
}

(3)“无头无循环单向链表”打印

void SListPrint(SListNode* plist)
{SListNode* cur = plist;while (cur != NULL){printf("%d->", cur->data);cur = cur->next;}printf("NULL\n");
}

(4)“无头无循环单向链表”尾插

void SListPushBack(SListNode** pplist, SLTDateType x)
{assert(pplist);//单独申请一个节点SListNode* chche = BuySListNode(x);//开始尾插节点SListNode* cur = *pplist;if (cur == NULL)//如果是空链表{*pplist = chche;}else//如果是非空链表{while (cur->next != NULL){cur = cur->next;}cur->next = chche;}/*千万不能直接cur找到整个链表的NULL,然后将新增加的节点赋值给cur(因为上一个节点的next依旧指向NULL,链表是断开的)*//*赋值的本质是拷贝*/
}

(5)“无头无循环单向链表”的头插

void SListPushFront(SListNode** pplist, SLTDateType x)
{//单独申请一个节点SListNode* chche = BuySListNode(x);chche->next = *pplist;//这个语句保证了两种情况都可以(NULL/非NULL)*pplist = chche;
}

(6)“无头无循环单向链表”的尾删

void SListPopBack(SListNode** pplist)
{assert(pplist && *pplist);//避免空指针和空链表if ((*pplist)->next == NULL)//单独处理只有一个节点的情况{free(*pplist);//哎呀,忘记释放内存了,补上*pplist = NULL;return;}//开始尾删节点SListNode* prev = *pplist;//这一步我一开始没有想到hhhhhh,prev是为了记录上一个节点SListNode* cur = *pplist;while (cur->next != NULL){prev = cur;cur = cur->next;}free(cur);//释放掉尾节点prev->next = NULL;//重新定义尾节点
}

(7)“无头无循环单向链表”头删

void SListPopFront(SListNode** pplist)
{assert(pplist && *pplist);SListNode* p = *pplist;*pplist = (*pplist)->next;//这里容易写出bug,因为“->”的优先级要比“*”高free(p);
}

(8)“无头无循环单向链表”查找

SListNode* SListFind(SListNode* plist, SLTDateType x)
{assert(plist);//保证不传空指针SListNode* cur = plist;if ((cur->next == NULL) && (cur->data == x)){return cur;}while (cur->next != NULL){if (cur->data == x){return cur;}cur = cur->next;}return NULL;
}

(9)“无头无循环单向链表”在pos位置之后插入x

// 分析思考为什么不在pos位置之前插入?tips:因为没有办法往前插入,这是单向链表结构上的缺陷
void SListInsertAfter(SListNode* pos, SLTDateType x)
{//单独申请一个节点assert(pos);SListNode* chche = BuySListNode(x);chche->next = pos->next;pos->next = chche;
}

(10)“无头无循环单向链表”删除pos位置之后的值

// 分析思考为什么不删除pos位置?tips:也是由于单向结构的原因,不能找回上一个节点
void SListEraseAfter(SListNode* pos)
{assert(pos && pos->next);SListNode* p1 = pos->next;SListNode* p2 = p1->next;pos->next = p2;free(p1);
}

(11)链表测试用例

void test()
{SListNode* a = NULL;SListPushFront(&a, 100);//头插SListPushFront(&a, 200);//头插SListPushFront(&a, 300);//头插SListPrint(a);//打印SListNode* p = SListFind(a, 300);//查找printf("%p\n", p);SListPopFront(&a);//头删SListPrint(a);//打印SListPopFront(&a);//头删SListPrint(a);//打印SListPopFront(&a);//头删SListPrint(a);//打印//SListPopFront(&a);//头删(头删了空链表)//SListPrint(a);//打印SListPushBack(&a, 3);//尾插SListPushBack(&a, 2);//尾插SListPushBack(&a, 1);//尾插SListPushBack(&a, 0);//尾插SListPushFront(&a, 100);//头插SListPushFront(&a, 200);//头插SListPushFront(&a, 300);//头插SListPrint(a);//打印SListPopBack(&a);//尾删SListPrint(a);//打印SListPopBack(&a);//尾删SListPopBack(&a);//尾删SListPopBack(&a);//尾删SListPrint(a);//打印SListPopBack(&a);//尾删SListPopBack(&a);//尾删SListPopBack(&a);//尾删//SListPopBack(&a);//尾删(尾删了空链表)SListPrint(a);//打印SListPushBack(&a, 3);//尾插SListPushBack(&a, 2);//尾插SListPushBack(&a, 1);//尾插SListPushBack(&a, 0);//尾插SListPushFront(&a, 100);//头插SListPushFront(&a, 200);//头插SListPushFront(&a, 300);//头插SListPushFront(&a, 400);//头插SListPrint(a);//打印p = SListFind(a, 100);//先查找得到节点地址SListInsertAfter(p, 4);//任意插入节点SListPrint(a);//打印SListPopFront(&a);//头删SListPopBack(&a);//尾删SListPopFront(&a);//头删SListPopBack(&a);//尾删SListPopFront(&a);//头删SListPopBack(&a);//尾删SListPopFront(&a);//头删SListPopBack(&a);//尾删p = SListFind(a, 4);//先查找得到节点地址SListInsertAfter(p, 100000);//任意插入节点SListPrint(a);//打印SListPopFront(&a);//头删SListPopFront(&a);//头删SListPrint(a);//打印SListPushBack(&a, 1);//尾插SListPushBack(&a, 0);//尾插SListPushFront(&a, 100);//头插SListPushFront(&a, 200);//头插SListPrint(a);//打印p = SListFind(a, 200);//先查找得到节点地址SListEraseAfter(p);SListPrint(a);//打印SListEraseAfter(p);SListPrint(a);//打印SListEraseAfter(p);SListPrint(a);//打印
}
int main()
{test();return 0;
}

3、有头双向有循环链表的实现(结构最复杂)

看我另外一篇文章:有头双向有循环链表的实现


http://www.ppmy.cn/news/48711.html

相关文章

Unity --- UGUI(Unity Graphical user interface)--- Canvas画布

1.UI --- User Interface --- 使用者与机器之间的交互界面 1.所谓的自适应系统指的是分辨率的适应: 比如在一个分辨率下做的UI放到另一个分辨率下显示时,如果没有自适应系统的话就会导致UI过大,过小,被辟成一半等等情况&#xff…

Excel函数培训目录

Excel快捷键★查找删除重复项★★数据透视表★★VLOOKUP 与 INDEX(MATCH())★★★多条件查找选择性粘贴 | 转置 与 运算 ★数据格式验证 ★ 函数 文本函数 文本函数ValueTRIM()去空TEXT()转换文本格式MID()中取LEFT()左取RIGHT()右取LEN()字符数量LENB()字节长度 查找函数 …

python批量下载怀俄明大学探空数据Wyoming soundings并处理

下载怀俄明大学的探空数据,之前用的是气象家园写的maltab脚本,但总是链接不上,而且有的站点需要用新网址,有的有需要用老网址,很麻烦,痛定思痛用决定终于用python了,主要有两种方式,…

傅里叶级数FS,连续时间傅里叶变换CTFT,离散时间傅里叶变换DTFT,离散傅里叶变换DFT,推导与联系(一)

本文主要从傅里叶级数 FS,连续时间傅里叶变换 CTFT,离散时间傅里叶变换 DTFT,以及离散傅里叶变换 DFT 之间的区别与联系进行了比较详细的讨论,主要注重于公式形式上的推导,略去了相关的图像示例,对于傅里叶…

「解析」Pytorch 自动计算 batchsize

日志是一个十分必要的操作,有助于后期分析实验结果,特别是在多台不同环境下训练,为了区分,还是十分有必要记录相关平台信息的,比如 hostname,Python版本信息,Pytorch版本信息等! im…

javascript中this指向问题

JavaScript中this指向问题 1、this指向window的情况 对于非箭头函数情况下,谁调用就指向谁,如果函数在全局作用域下调用,里面的this就是window。 在全局作用域下,this window function sum() {console.log(this); }sum(); // windowconsole.log(this…

educoder实训——函数【1】

文章目录 无参无返回值函数任务描述代码无参有返回值函数任务描述代码有参有返回值函数任务描述代码多参函数任务描述代码任意数量参数任务描述代码pow函数详解任务描述编程要求代码最大公约数任务描述编程要求代码最小公倍数<

ReetrantLock源码剖析_03公平锁、非公平锁

一直努力就会有offer&#xff0c;一直努力就会有offer&#xff0c;一直努力就会有offer&#xff01; 文章目录 ReetrantLock公平锁代码解析ReetrantLock公平锁执行流程ReetrantLock非公平锁代码解析ReetrantLock非公平锁执行流程公平锁与非公平锁的比较 ReetrantLock公平锁代码…