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

双向链表----“双轨联动,高效运行” (第九讲)

一. 链表的分类

 链表的结构非常多样,按照以下的组合起来就有8种:

(1)带头或者不带头

  

   带头链表中的“头结点”,不存储任何有效的数据,只是用来占位的,我们称之为“哨兵位”。

   在前面的文章中,有时候表述“头结点”,但实际上单链表中把第一个节点称为“头结点”这种说法是错误的。

(2)单向或者双向

(3)循环或者不循环

      虽然这么多的链表结构,但是我们最常用的有两种:单链表(不带头单向不循环链表)和双链表(带头双向循环链表)。


二. 双向链表

2.1概念与结构

   双向链表由一个一个的节点组成,这里的节点包括3个部分。

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

     注:当双向链表为空时,这里表示双链表中只有一个哨兵位,且它的前驱指针和后继指针都指向自身。图示如下:

2.2 双向链表的初始化

#pragma once
#include <stdio.h>
#include <stdlib.h>
typedef int LTDataType;
typedef struct ListNode {LTDataType data;LTNode* prev;LTNode* next;
}LTNode;//1.初始化
void LTInit(LTNode** phead);
#include "List.h"
void LTInit(LTNode** pphead)
{*pphead = (LTNode*)malloc(sizeof(LTNode));if (*pphead == NULL){perror("malloc fail!");exit(1);}(*pphead)->data = -1;//哨兵位节点不存储任何有效数据,-1为无效(*pphead)->next = (*pphead)->prev = NULL;
}

  现在,我们已经有了一个空的双向链表,接下来,我们就要对其“增删改查”了。注意:在双向链表中,增删改查都不会改变哨兵位节点

2.3 尾插

LTNode* LTBuyNode(LTDataType x)
{LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));while (newnode == NULL){perror("malloc fail!");exit(1);}newnode->prev = newnode->next = newnode;newnode->data = x;return newnode;
}
//2.尾插
LTNode* pushback(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = LTBuyNode(x);newnode->prev = phead->prev;newnode->next = phead;phead->prev->next = newnode;phead->prev = newnode;
} 

    注:在尾插时,我们要特别注意几个指针的顺序,应该先处理newnode的前驱指针和后继指针,再处理原来双链表中尾节点的后继指针,使其指向newnode,最后处理头节点的前驱指针,使其指向最后一个节点(此时也就是newnode)。(此处一定要注意顺序,如果错乱,就有可能找不到原来的头节点)。

2.4 头插

  头插时,节点是插在哨兵位的前面,还是插在哨兵位和下一个节点的中间?显然,答案是后者。因为哨兵位不存储有效数据,并不可以算作是双链表的第一个节点。

//3.头插
LTNode* pushfront(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = LTBuyNode(x);newnode->next = phead->next;newnode->prev = phead;phead->next->prev = newnode;phead->next = newnode;
}

2.5 尾删

//4.判断链表是否为空
bool LTEmpty(LTNode* phead)
{assert(phead);return phead->next == phead;
}
//5.尾删
void popback(LTNode* phead)
{if (!LTEmpty(phead)){LTNode* del = phead->prev;del->prev->next = phead;phead->prev = del->prev;free(del);del = NULL;}
}

2.6 打印双链表

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

2.7 头删

//7.头删
LTNode* popfront(LTNode* phead)
{assert(!LTEmpty(phead));LTNode* del = phead->next;del->next->prev = phead;phead->next = del->next;free(del);del = NULL;
}

2.8 查找

//8.查找
LTNode* find(LTNode* phead, LTDataType x)
{assert(phead);LTNode* pcur = phead->next;while (pcur != phead){if (pcur->data == x){return pcur;}pcur = pcur->next;}//未找到return NULL;
}

2.9 在指定位置之后插入数据

//9.在pos位置之后插入数据
void LTInit(LTNode* pos, LTDataType x)
{assert(pos);LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));newnode->prev = pos;newnode->next = pos->next;pos->next->prev = newnode;pos->next = newnode;
}

2.10 删除指定位置的节点

//10.删除pos位置的节点
void erase(LTNode* pos)
{assert(pos);pos->prev->next = pos->next;pos->next->prev = pos->prev;free(pos);pos = NULL;
}

2.11 销毁双链表

//11.销毁双链表
void LTDestory(LTNode** pphead)
{LTNode* pcur = (*pphead)->next;while (pcur != *pphead){LTNode* next = pcur->next;free(pcur);pcur = next;}//销毁头节点free(*pphead);*pphead = NULL;
}

三. 代码改进

    由上述的代码可以看出,除了初始化和释放形参是二级指针,其余功能实现形参都是一级指针,那我们为了保持接口一致性,能不能初始化和释放这两个功能也弄成一级指针呢?具体方法如下:

3.1 初始化

//1.初始化
LTNode* LTInit()
{LTNode* phead = LTBuyNode(-1);return phead;
}

3.2 销毁

void destory(LTNode* phead)
{LTNode* pcur = (phead)->next;while (pcur != phead){LTNode* next = pcur->next;free(pcur);pcur = next;}//销毁头节点free(phead);phead = NULL;
}

但是,这种写法需要自己手动将实参置为空。


四. 顺序表与链表分析

不同点顺序表链表(单链表)
存储空间上物理上一定连续逻辑上连续,但物理上不一定连续
随机访问支持O(1)不支持:
任意位置插入或者删除元素可能需要搬移元素,效率低O(N)只需修改指针指向
插入动态顺序表,空间不够时需要扩容和空间浪费没有容量的概念,按需申请释放,不存在空间浪费
应用场景元素高效存储+频繁访问任意位置高效插入和删除

  以上就是今天的内容,到目前为止,顺序表和链表就告一段落啦~喜欢的朋友们可以一键三连哦~

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

相关文章:

  • CEX-DEX 稳定币套利模型
  • 【C++STL :list类 (一) 】C++98 完全指南:std::list 详解与源码剖析
  • shell脚本02
  • 【PM2】PM2 集群模式适用的场景
  • 保定网站建设方案外包南宁网站建设哪
  • 做网站建设费用预算百度贴吧网站开发需求分析
  • Docker 仓库详解与实战配置
  • dockerfile实操案例
  • linux学习笔记(25)——线程安全
  • ubuntu20.04地平线OE3.2.0 GPU Docker使用
  • [VoiceRAG] Azure | 使用`azd`部署应用 | Dockerfile
  • Docker 环境下 GeoPandas/Fiona 报错
  • Docker简易教程
  • vps 网站发布直播软件app下载免费
  • DORIS 服务器宕机重启后出现的问题
  • 网络安全审计技术原理与应用
  • 手机上做网站南宁品牌网站设计公司
  • 第五部分:VTK高级功能模块(第135章 Imaging模块 - 图像处理类)
  • 如何通过 5 种有效方法同步 Android 和 Mac
  • AJAX 知识篇(2):Axios的核心配置
  • 招商网站建设公司申请注册商标的流程
  • 网页美工课程seo网站优化师
  • 海外关键词规划SEO工具
  • AI学习日记——卷积神经网络(CNN):卷积层与池化层的实现
  • iOS 26 系统流畅度实战指南|流畅体验检测|滑动顺畅对比
  • JS中new的过程发生了什么
  • 系统白名单接口添加自定义验证(模仿oauth2.0),防安全扫描不通过
  • 校园服装网站建设预算手机软件应用市场
  • 【AI论文】ExGRPO:从经验中学习进行推理
  • 连接两个世界:QIR——量子-经典混合计算的编译器桥梁