[C/C++]数据结构 链表(单向链表,双向链表)

前言:

        上一文中我们介绍了顺序表的特点及实现,但是顺序表由于每次扩容都是呈二倍增长(扩容大小是自己定义的),可能会造成空间的大量浪费,但是链表却可以解决这个问题.

概念及结构:

         链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的

注意:

  1. 链式结构在逻辑上是连续的,但是在物理上不一定连续
  2. 结点一般都是从堆上申请出来的
  3. 从堆上申请的空间是按照一定的策略来分配的,两次申请的空间可能连续也可能不连续

    分类:

  1. 单向或双向
  2. 带头或不带头
  3. 循环或不循环

    虽然链表有这么多种结构,但是在实际中最常用的还是两种结构:

  • 无头单向非循环链表:

          结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈       希桶、图的邻接表等等。       

  •  带头双向循环链表:

       结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单

无头单向非循环链表

接口:

typedef int SLTDateType;
typedef struct SListNode
{SLTDateType data;struct SListNode* next;
}SListNode;// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x);
// 单链表打印
void SListPrint(SListNode* plist);
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x);
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x);
// 单链表的尾删
void SListPopBack(SListNode** pplist);
// 单链表头删
void SListPopFront(SListNode** pplist);
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x);
// 单链表在pos位置之后插入x
void SListInsertAfter(SListNode** pphead,SListNode* pos, SLTDateType x);
// 单链表删除pos位置之后的值
void SListEraseAfter(SListNode** pphead,SListNode* pos);
// 在pos的前面插入
SListNode* SListInsertFront(SListNode** pphead, SListNode* pos, SLTDateType x);
// 删除pos位置
void SLTErase(SListNode** pphead, SListNode* pos);
//单链表摧毁
void SLTDestroy(SLNode** pphead);

1.动态申请结点

SListNode* BuySListNode(SLTDateType x)
{SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));if (newnode == NULL){perror("malloc");return;}newnode->data = x;newnode->next = NULL;
}

2.单链表打印

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

3.单链表尾插

void SListPushBack(SListNode** pplist, SLTDateType x)
{SListNode* newnode = BuySListNode(x);if (*pplist == NULL){*pplist = newnode;}else{SListNode* tail = *pplist;while (tail->next){tail = tail->next;}tail->next = newnode;}
}

4.单链表头插

void SListPushFront(SListNode** pplist, SLTDateType x)
{SListNode* newnode = BuySListNode(x);newnode->next = *pplist;*pplist = newnode;
}

5.单链表尾删

void SListPopBack(SListNode** pplist)
{assert(*pplist);SListNode* pre = NULL;SListNode* tail = *pplist;if ((*pplist)->next == NULL){*pplist = NULL;}else{/* while (tail->next){pre = tail; tail = tail->next;}free(tail);tail = NULL;pre->next = NULL;*/SListNode* tail = *pplist;while (tail->next->next){tail = tail->next;}free(tail->next);tail->next = NULL;} 
}

6.单链表头删

void SListPopFront(SListNode** pplist)
{assert(pplist);if ((*pplist)->next == NULL){*pplist = NULL;}else{SListNode* ret = ((*pplist)->next);free(*pplist);*pplist = ret;}
}

7.单链表查找

SListNode* SListFind(SListNode* plist, SLTDateType x)
{assert(plist);SListNode* ret = plist;while (ret->data != x&&ret->next){ret=ret->next;}if (ret->next == NULL && ret->data != x)return NULL;elsereturn ret; 
}

8.摧毁单链表

void SLTDestroy(SListNode** pphead)
{SListNode* cur = *pphead;SListNode* ret = NULL;while (cur){ret = cur->next;free(cur);cur = ret;}*pphead = NULL;
}

9.在pos位置之后插入x

// 单链表在pos位置之后插入x
void SListInsertAfter(SListNode** pphead,SListNode* pos, SLTDateType x)
{assert(pphead);//检测插入位置是否存在assert(pos); assert(*pphead);SListNode* newnode=BuySListNode(x);newnode->next = pos->next;pos->next = newnode;
}

10.删除pos位置之后的值

// 单链表删除pos位置之后的值
void SListEraseAfter(SListNode** pphead,SListNode* pos)
{assert(pphead);assert(pos);assert(pos->next);assert(*pphead);SListNode* tmp = pos->next;pos->next = pos->next->next;free(tmp);tmp = NULL;
}

11.在pos位置之前插入x

SListNode* SListInsertFront(SListNode** pphead, SListNode* pos, SLTDateType x)
{assert(pphead);assert(pos);assert(*pphead);SListNode* newnode = BuySListNode(x);SListNode* pre = *pphead;if (*pphead == pos){SListPushFront(pphead,x);}else{while (pre->next != pos){pre = pre->next;}newnode->next = pos;pre->next = newnode;}}

12.删除pos位置

void SLTErase(SListNode** pphead, SListNode* pos)
{assert(pphead);assert(pos);assert(*pphead);if (*pphead == pos){SListPopFront(pphead);}else{SListNode* pre = *pphead;while (pre->next != pos){pre = pre->next;}pre->next = pos->next;free(pos);pos = NULL;}}

 

带头双向循环链表

接口:

// 带头+双向+循环链表增删查改实现
typedef int LTDataType;
typedef struct ListNode
{LTDataType _data;struct ListNode* _next;struct ListNode* _prev;
}ListNode;// 创建返回链表的头结点.
ListNode* ListCreate();
// 双向链表销毁
void ListDestory(ListNode* pHead);
// 双向链表打印
void ListPrint(ListNode* pHead);
// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x);
// 双向链表尾删
void ListPopBack(ListNode* pHead);
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x);
// 双向链表头删
void ListPopFront(ListNode* pHead);
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x);
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos);

1.创建返回链表的头节点

ListNode* ListCreate(LTDataType x)
{ListNode* node = (ListNode*)malloc(sizeof(ListNode));if (node == NULL){perror("malloc");exit(-1);}node->_data = x;node->_next = NULL;node->_prev = NULL;return node;
}

2.双向链表销毁

void ListDestory(ListNode* pHead)
{assert(pHead);ListNode* cur = pHead->_next;while (cur != pHead){ListNode* next = cur->_next;free(cur);cur = next;}free(pHead);
}

3.双向链表打印

void ListPrint(ListNode* pHead)
{assert(pHead);ListNode* cur = pHead->_next;while (cur != pHead){printf("%d <-> ", cur->_data);cur = cur->_next;}
}

4.双向链表尾插

void ListPushBack(ListNode* pHead, LTDataType x)
{ListNode* newnode = ListCreate(x);ListNode* tail = pHead->_prev;     //尾指针tail->_next = newnode;newnode->_next = pHead;newnode->_prev = tail;pHead->_prev = newnode;
}

5.双向链表尾删

void ListPopBack(ListNode* pHead)
{assert(pHead);assert(pHead->_next!=pHead);ListNode* tail = pHead->_prev;pHead->_prev = tail->_prev;tail->_prev->_next = pHead;free(tail);tail = NULL;
}

6.双向链表头插

void ListPushFront(ListNode* pHead, LTDataType x)
{assert(pHead);ListNode* newnode = ListCreate(x);newnode->_next = pHead->_next;pHead->_next->_prev = newnode;pHead->_next = newnode;newnode->_prev = pHead;
}

7.双向链表头删

void ListPopFront(ListNode* pHead)
{ListNode* pHeadNext = pHead->_next;pHeadNext->_next->_prev = pHead;pHead->_next = pHeadNext->_next;free(pHeadNext);pHeadNext = NULL;
}

8.双向链表查找


ListNode* ListFind(ListNode* pHead, LTDataType x)
{assert(pHead);ListNode* cur = pHead->_next;while (cur != pHead){if (cur->_data == x)return cur;cur = cur->_next;}return NULL;
}

9.双向链表在pos的前面进行插入

void ListInsert(ListNode* pos, LTDataType x)
{assert(pos);ListNode* posprev = pos->_prev;ListNode* newnode = ListCreate(x);newnode->_next = pos;pos->_prev = newnode;posprev->_next = newnode;newnode->_prev = pos->_prev;
}

10.双向链表删除pos位置的节点

void ListErase(ListNode* pos)
{assert(pos);ListNode* posprev = pos->_prev;ListNode* posnext = pos->_next;posprev->_next = posnext;posnext->_prev = posprev;free(pos);
}

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

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

相关文章

cs与msf联动

实验环境 cs4.4(4.5版本不知道为啥实现不了) cs服务器与msf在同一台vps上 本地win7虚拟机 cs派生会话给msf 首先cs正常上线win7&#xff0c;这就不多说了&#xff0c;然后说如何将会话派生给msf cs准备 选择Foreign&#xff0c;这里可以选HTTP&#xff0c;也可以选HTTPS…

AnimateDiff搭配Stable diffution制作AI视频

话不多说&#xff0c;先看视频 1. AnimateDiff的技术原理 AnimateDiff可以搭配扩散模型算法&#xff08;Stable Diffusion&#xff09;来生成高质量的动态视频&#xff0c;其中动态模型&#xff08;Motion Models&#xff09;用来实时跟踪人物的动作以及画面的改变。我们使用 …

基于和声算法优化概率神经网络PNN的分类预测 - 附代码

基于和声算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于和声算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于和声优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神经网络的光滑…

【开源】基于JAVA的快递管理系统

项目编号&#xff1a; S 007 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S007&#xff0c;文末获取源码。} 项目编号&#xff1a;S007&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容2.1 数据中心模块2.2 快递类型模块2.3 快…

CTFhub-RCE-综合过滤练习

%0a、%0d、%0D%0A burp 抓包 修改请求为 POST /?127.0.0.1%0als 列出当前目录 返回包 http://challenge-135e46015a30567b.sandbox.ctfhub.com:10800/?ip127.0.0.1%0acd%09*here%0ac%27a%27t%09* _311632412323588.php

【自用总结】正项级数审敛法的总结

注&#xff1a;收敛半径的求法就是lim n->∞ |an1/an| ρ&#xff0c;而ρ1/R&#xff0c;最基本的不能忘。 比较判别法&#xff1a;从某项起&#xff0c;该级数后面的项均小于等于另一级数&#xff0c;则敛散性可进行一定的比较 可以看到&#xff0c;比较判别法实际上比较…

调整COSWriter解决X-easypdf / PDFBOX生成大量数据时OOM问题

背景 业务需要生成一个15W数据左右的PDF交易报表。希望我们写在一个文件里&#xff0c;不拆分成多个PDF文件。 使用的技术组件 <dependency><groupId>wiki.xsx</groupId><artifactId>x-easypdf-pdfbox</artifactId><version>2.11.10<…

Linux通过端口号找到对应的服务及其安装位置

Linux服务器中&#xff0c;通过端口号找到对应的服务及其安装位置&#xff0c;需要两步操作&#xff0c;如下&#xff1a; 第一步&#xff1a;根据端口号&#xff0c;确定对应的进程号&#xff08;以redis服务为例&#xff09; netstat -antup|grep 6379第二步&#xff1a;通…

Zabbix5.0部署

环境 主机名 IP 类型server01192.168.134.165zabbix-serverserver02 192.168.134.166zabbix-agent 官方部署文档 1 .安装yum源 [rootserver01 ~]# rpm -Uvh https://repo.zabbix.com/zabbix/5.0/rhel/7/x86_64/zabbix-rel…

cp: can‘t stat ‘/usr/share/zoneinfo/Asia/Shanghai‘: No such file or directory

目录 问题描述问题分析解决方案容器时区验证 问题描述 使用下面的 Dockerfile 为 youlai-boot 项目制作镜像设置容器时区报错。 # 基础镜像 FROM openjdk:17-jdk-alpine # 时区修改 RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \&& echo Asia/Sha…

JVM:字节码文件,类的生命周期,类加载器

JVM&#xff1a;字节码文件&#xff0c;类的生命周期&#xff0c;类加载器 为什么要学这门课程 1. 初识JVM1.1. 什么是JVM1.2. JVM的功能1.3. 常见的JVM 2. 字节码文件详解2.1. Java虚拟机的组成2.2. 字节码文件的组成2.2.1. 以正确的姿势打开文…

需求管理>需求的变更流程

1.需求的变更流程 一个大型软件系统的需求总是有变化的。为了降低项目开发的风险&#xff0c;需要一个好的变更控制过程。如下图所示为需求变更管理过程。 在需求管理过程中需求的变更是受严格管控的&#xff0c;其流程为&#xff1a; 1、问题分析和变更描述。这是识别和分析需…