当前位置: 首页 > news >正文

(数据结构)线性表(下):链表分类及双向链表的实现

链表分类及双向链表的实现

  • 链表的分类
  • 双向链表
    • 结点结构
    • 链表结构
    • 初始化
    • 遍历/打印
    • 尾插
    • 头插
    • 尾删
    • 头删
    • 查找
    • 在pos位置之后插入
    • 删除pos位置的结点
    • 销毁双向链表
  • 顺序表与链表的分析(线性表)

链表的分类

请添加图片描述
一共有222=8种链表
在这里插入图片描述
next:指向下一个结点(后继结点)
prev:指向前一个结点(前驱结点)
在这里插入图片描述
头结点不存储数据,作为“哨兵位”。
(单链表的第一个结点不能叫头结点!因为其存储了数据!)
在这里插入图片描述
不循环链表的尾结点next为空,循环链表的尾结点next不为空

  • 虽然有这么多的链表的结构,但是我们实际中最常用还是两种结构:单链表(单向不带头不循环链表)和双向链表(双向带头循环链表)
  1. 单链表:结构简单,⼀般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。
  2. 双向链表:结构最复杂,⼀般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了。

双向链表

结点结构

struct ListNode
{int data;struct ListNode* next;struct ListNode* prev;
}

链表结构

双向带头循环链表:
在这里插入图片描述
特点:

  1. 头结点的prev指向尾结点,尾结点的next指向头结点。表达为:ptail=phead->prev phead=ptail->next
  2. 循环链表的特性:如果遍历条件是pcur!=NULL,链表将无线循环遍历下去。
  3. phead(即双向链表头结点的地址)永远不会发生改变。(但是头结点中的成员是可以改变的!)

初始化

双向链表为空的情况下只有一个哨兵位,且next和prev指针都指向自己。
在这里插入图片描述
(如果连哨兵位都没有的话,这就是单链表而不是双向链表)

//形成一个新的结点,频繁使用,独立出来
LTNode* buyNode(LTDataType x) {LTNode* node = (LTNode*)malloc(sizeof(LTNode));if (node == NULL) {perror("malloc fail!");exit(1);}node->data = x;node->next = node->prev = node;return node;
}
/双向链表的初始化法1
void LTInit01(LTNode** pphead) {//只有传地址才能真正改变phead!*pphead = buyNode(-1);//随便给一个data就行了,因为头结点的数据是不会用到了
}
//双向链表的初始化法2
LTNode* LTInit02() {LTNode* phead = buyNode(-1);return phead;
}

遍历/打印

双向链表是循环链表,所以循环条件不能再是pcur!=NULL

//打印双向链表
void LTPrint(LTNode* phead)
{LTNode* pcur = phead->next;while (pcur != phead) {printf("%d -> ", pcur->data);pcur = pcur->next;}
}

尾插

添加元素时,需要先分情况为链表为空和链表非空,因为有可能操作是不同的。(但是尾插的情况下操作相同就可以合并)

  • 修改顺序?——修改指针指向时,先修改对原链表无影响的指针
  1. newnode的prev、next可以先定
  2. 再修改尾结点的next和头结点的prev
//尾插
//传入的phead结点不会发生改变,参数就只传一级指针足够
//如果传入的phead指针需要发生改变(LTInit01),参数就需要传二级
void LTPushBack(LTNode* phead, LTDataType x) {//如果传入的phead为空,则传入的根本不是双向链表,直接断言报错assert(phead);LTNode* newnode = buyNode(x);newnode->next = phead;LTNode* ptail = phead->prev;newnode->prev = ptail;ptail->next = newnode;phead->prev = newnode;
}

头插

头插是指插入一个结点作为链表的第一个有效节点
当然不能插在phead前面!因为我们知道phead->prev其实是尾结点。

  • 与尾插相同,修改指针指向时,先修改对原链表无影响的指针
    在这里插入图片描述
//头插
void LTPushFront(LTNode* phead, LTDataType x)
{LTNode* newnode = buyNode(x);newnode->prev = phead;newnode->next = phead->next;phead->next->prev = newnode;phead->next = newnode;
}

尾删

进行删除前一定要先探空——防止导致头结点被删除导致链表销毁等异常问题
//尾删

void LTPopBack(LTNode* phead)
{assert(!LTEmpty(phead));LTNode* del = phead->prev;//把尾结点(即要删除的结点)单独写出来避免代码难读del->prev->next = phead;phead->prev = del->prev;free(del);
}

头删

void LTPushFront(LTNode* phead, LTDataType x)
{LTNode* newnode = buyNode(x);newnode->prev = phead;newnode->next = phead->next;phead->next->prev = newnode;phead->next = newnode;
}

查找

LTNode* LTFind(LTNode* phead, LTDataType x)
{assert(phead);LTNode* pcur = phead->next;while (pcur != phead){if (pcur->data == x){return pcur;}pcur = pcur->next; }return NULL;
}

在pos位置之后插入

//在pos位置之后插入
void LTInsert(LTNode* pos, LTDataType x)
{assert(pos);LTNode* newnode = buyNode(x);newnode->prev = pos;newnode->next = pos->next;pos->next->prev = newnode;pos->next = newnode;
}

删除pos位置的结点

//删除pos位置的结点
void LTErase(LTNode* pos, LTDataType x)
{assert(pos);pos->next->prev = pos->prev;pos->prev->next = pos->next;free(pos);pos = NULL;
}

销毁双向链表

//销毁01 因为需要释放头结点,所以需要传二级指针
void LTDesTroy(LTNode** pphead)
{LTNode* pcur = (*pphead)->next;while (pcur != *pphead){LTNode* next = pcur->next;free(pcur);pcur = next;}free(*pphead);*pphead = NULL;
}
//销毁02 为保持接口一致性,建议统一参数指针都为一级
//但是需要手动把phead实参置空,否则本函数里只能改变形参
void LTDesTroy(LTNode* phead)
{LTNode* pcur = phead->next;while (pcur != phead){LTNode* next = pcur->next;free(pcur);pcur = next;}free(phead);
}

顺序表与链表的分析(线性表)

在这里插入图片描述

http://www.dtcms.com/a/469875.html

相关文章:

  • 阿里云短信服务配置说明
  • Java+SpringBoot+Dubbo+Nacos快速入门
  • 【开题答辩全过程】以 办公管理系统为例,包含答辩的问题和答案
  • 天创网站做网站 数据标准
  • 做除尘骨架的网站网页一般用什么语言编写
  • SciPy 常量模块
  • 记录一次在Win7系统中使用C#中的HttpWebRequest连接缓慢、超时等问题(httpclient和restsharp也存在同样的问题)
  • Spring Boot 3零基础教程,Spring Intializer,笔记05
  • spring boot 2.x 与 spring boot 3.x 及对应Tomcat、Jetty、Undertow版本的选择(理论)
  • 织梦 网站栏目管理 很慢国内免费域名申请
  • 建设企业网站的好处是什么门户网站建设的成果
  • 【BUG排查】基于RH850F1KMS1的主控出现系统中断错误,调试FEIC的值为0x11
  • C++变量命名详解
  • 2.c++面向对象(三)
  • 自动化测试系统Parasoft航空设备行业案例:减少75%的BUG
  • Git Commit Message 最佳实践:从一次指针Bug说起
  • 网站设计专业知识技能传奇 网页游戏排行榜
  • 漳州北京网站建设公司wordpress小工具九宫格
  • 多智能体协作中的数据交互艺术:构建高效协同的智能系统
  • 人工智能大模型的“通俗理解”
  • 网站后台怎么做水印图片石家庄最新大事
  • 项目学习总结:platform方式驱动框架、pc版和arm版连通ONENET方式、wireshark抓包mqtt、ONENET创建产品、双网卡配置
  • Transformers包常用函数讲解
  • 在昇腾910B服务上部署搭建适配PDF解析工具Mineru2.5开源项目
  • Vue项目中将界面转换为PDF并导出的实现方案
  • 黄山市非遗网站策划书推广普通话作文
  • 深度学习基础:从原理到实践——第二章神经网络(中)
  • 从传统Linux部署到容器化:实践对比与工程化指南
  • Python 高效实现 Excel 与 CSV 互转:用自动化提升效率
  • php购物网站开发uml图注册页面设计代码