线性表的链式存储

目录

1.链式存储结构与顺序存储结构的比较

2.单链表、双链表与循环链表

3.单链表的实现


1.链式存储结构与顺序存储结构的比较

顺序存储的特点:已物理相邻表示逻辑关系,任一元素均可随机存取。

链式存储的特点:用一组物理位置任意的存储单元来存放线性表的数据元素,这组存储单元可以是连续的也可以是不连续的,甚至可以分布在内存中的任意位置上,链表中元素的逻辑次序和物理次序不一定相同。

两种存储方式可以用下图来辅助理解。

顺序存取:查找第i个位置的数据,需要通过从第一个到第i个,顺序查找,所以存取的时候都是需要按顺序进行,所以叫顺序存取。

随机存取:随便一个i位置的数据,可以通过查找数组第 (i-1)个来查找,存取的时候同样可以存第i个位置或者取第i个位置的元素,所以叫随机存取。

可以用以下图来辅助理解:

2.单链表

单链表,指结点只有一个指针域的链表,也可以称为线性链表。每个单链表都有一个头指针,也就是链表的首地址,其结点分为两个部分,一个是指针域一个是数据域(如下图所示),其中指针域存下一个结点的地址,而数据域为该结点存放的元素。

用单链表的方式来表示线性表,有两种方式,带头结点跟不带头结点。不带头结点,顾名思义就是链表的头指针指向的下一个即为首元素结点;而带头结点的链表则头指针指向头结点,而头结点再指向首元素结点。如下图所示:

带头结点的好处:

便于首元素结点的处理:首元素结点的地址保存在头结点的指针域中,所以在链表中的第一个位置上的操作和其他位置的一致,无需进行特殊处理。

便于空表和非空表的统一处理:无论链表是否为空,头指针都只想头结点的非空指针,因此空表和非空表的处理也就一致了。

3.单链表的实现

单链表定义:用结构体的方式定义,定义一个数据域跟一个指针域。这里实现的是不带头结点的单链表。

这段代码的ElenType为data的类型(相当于结构体的自定义类型);

typedef int ElemType; //定义数据类型
typedef struct LNode  //声明结点的类型和指向结点的指针类型
{ElemType data;       //结点的数据域struct LNode* next;  //结点的指针域
}LNode,*LinkList;        //LinkList为指向结构体Lnode的指针

注:定义的结构体LNode的类型名称为LNode其指针的为LinkList。即  *LNode = LinkList ,因为后面的函数多数都是对链表的指针域进行操作,所以定义指针类型LinkList方便后面的操作。

定义函数的操作:


#define TRUE 1
#define FALSE 0
#define VETERY 1
#define DEFEAT 0
#define NOFINE 0
typedef int Status;Status InitLinkList(LinkList L); //初始化单链表
LinkList List_HeadInsert(LinkList L); //头插入法
LinkList List_TailInsert(LinkList L); //尾插入法
LNode* GetElem(LinkList L, int i);  //按序号查找结点值
LNode* LocateElem(LinkList L, ElemType e); //按照结点值查找结点
void Delete_Node(LinkList L, int i); //按序号删除结点
void Print_Node(LinkList L);  //打印单链表
int Linklength(LinkList L);   //计算结点个数
int ListEmpty(LinkList L);    //判断单链表是否为空
Status ClearList(LinkList L); //清空单链表
Status DestroyList(LinkList L);  //销毁单链表

单链表的基本操作:对单链表进行初始化、建立、判断空值、取值、查找、删除、打印、求表长、清空、销毁十一个操作进行编写代码。

代码块粘贴在最后

初始化,即开辟一个空间,创建单链表的头指针;

Status InitLinkList(LinkList L) //初始化单链表
{L = (LinkList)malloc(sizeof(LNode));  //开辟一个指针L->next = NULL; //单链表没有下一个指针return VETERY;
}

注:(强制转换的类型)malloc(sizeof(类型空间)),malloc是C语言进行开辟空间的函数,如果还不理解的话,可以去看看官方文档的解释。

判断单链表是否为空,只要头指针指向的下一个地址不为空,则还有元素结点在。

int ListEmpty(LinkList L)    //判断单链表是否为空
{if (L->next){printf("链表不为空!\n");return FALSE;}else{printf("链表为空!\n");return TRUE;}
}

清空单链表

        清空单链表采用两个指针进行操作,通过定义p指针为L->next,则为首元素结点,然后用另外一个q指针指向p->next则为第二个元素结点,然后释放p指针free(p),在将q指针赋予给p指针,然后通过不断释放p指针从而实则释放全部的元素结点。下图辅助理解:

Status ClearList(LinkList L) //清空单链表
{LinkList p, q;p = L->next;while (p){q = p->next;free(p);p = q;}L->next = NULL;return VETERY;
}

销毁单链表

        销毁单链表将整个单链表的空间都释放掉,因此是从头指针开始往后释放每一个结点。

Status DestroyList(LinkList L)  //销毁单链表
{LinkList p;while (L){p = L;L = L->next;free(p);}return VETERY;
}

求单链表长度

int Linklength(LinkList L)   //求单链表长度
{int k = 0;while (L->next != NULL){k++;L = L->next;  }return k;
}

打印单链表

void Print_Node(LinkList L)   //打印单链表
{while (L->next != NULL){L = L->next;printf("%d ", L->data);}printf("\n");
}

头插法建立单链表

通过从第一个元素结点进行插入结点,可以由下图辅助理解:

LinkList List_HeadInsert(LinkList L)//头插入法
{LNode *new;int i = 10;for (i = 0; i < 10; i++){new = (LNode*)malloc(sizeof(LNode));   //开辟一个新指针空间new->data = i;    //给指针的数据域赋值new->next = L->next;   //将新结点的下一个指针指向首元素结点,则代替首元素结点L->next = new;  //头指针指向新结点,即新节点为首元素结点}return L;
}

尾插法建立单链表

顾名思义,就是从尾部插入元素,通过下图进行辅助理解:

LinkList List_TailInsert(LinkList L) //尾插入法
{LNode* new;LNode* tail = L;for (int i = 0; i < 10; i++){new = (LinkList)malloc(sizeof(LNode));new->data = i;tail->next = new; //将最后一个结点下一个指向new指针tail = new;       //最后一个指针为new指针}tail->next = NULL;    //最后一个指针指向NULLreturn L;
}

 按序号查找结点值

通过序号 i 来查找相对应的结点值,通过第一个到第 i 个结点,从而返回结点地址,通过结点地址可以获取到对应的结点值。

LinkList GetElem(LinkList L, int i)    //按序号查找结点值
{int j = 1;LNode* p = L->next;  //定义辅助指针pif (i == 0)          //如果为0,则返回头指针地址return L;if (i < 1)           //如果小于1则返回NULL空指针return NULL;while (p && j < i)   //在j>=i和p不为NULL之前,循环继续{p = p->next;     //p指下一个指针j++;             //j+1}                    //查找得到的话为j=i时返回,查找不到,则一直到p为NULLreturn p;
}

按值查找表结点

通过第一个结点的data开始与目标值e进行对比,直到查找到,则返回结点值。若果没有则返回NULL。

LinkList  LocateElem(LinkList L, ElemType e) //按照结点值查找结点
{LNode* p = L->next;while (p->data != e && p != NULL){p = p->next;}return p;
}

按序号删除结点

通过GetElem函数查找结点的前驱指针,然后将前驱指针 p 指向待删除结点 q 的后继指针,从而释放待删除结点free(q)。通过下图进行辅助理解:

void Delete_Node(LinkList L, int i) //按序号删除结点
{LinkList  p = GetElem(L, i - 1);  //查找前驱结点LinkList  q;                    q = p->next;                      //令q等于待删除结点p->next = q->next;                //将p的下一个指向q的下一个free(q);                          //删除q
}

代码:

test.c

#include "LinkList.h"int main()
{LNode L;           //定义链表LInitLinkList(&L);  //初始化链表//List_HeadInsert(&L);   //头插法创建链表//Print_Node(&L);        //打印链表List_TailInsert(&L);     //尾插法创建链表Print_Node(&L);          //打印链表 LNode* p = GetElem(&L, 3);    //查找i = 3 位置的指针printf("查找成功3的数据为%d \n", p->data);    //打印3位置的元素数据LNode* q = LocateElem(&L, 5);                      //查找值为5的元素的指针printf("查找成功5的位置为%p \n", q);          //打印查找的指针值int l = LinkLength(&L);                             //链表长度printf("%d\n", l);                            //打印链表长度Delete_Node(&L, 4);                             //删除i = 4的结点printf("删除成功!\n");                        Print_Node(&L);                                     //打印链表int i = LinkLength(&L);           printf("%d\n", i);                           //查看链表长度ClearList(&L);                                      //清空链表if(!ListEmpty(&L))                                  //判断链表是否为空Print_Node(&L);                                 //不为空则打印链表return 0;
}

LinkList.,h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#define TRUE 1
#define FALSE 0
#define VETERY 1
#define DEFEAT 0
#define NOFINE 0typedef int ElemType; //定义数据类型
typedef int Status;
typedef struct LNode
{ElemType data;       //结点的数据域struct LNode* next;  //结点的指针域
}LNode,*LinkList;        //LinkList为指向结构体Lnode的指针Status InitLinkList(LinkList L); //初始化单链表
LinkList List_HeadInsert(LinkList L); //头插入法
LinkList List_TailInsert(LinkList L); //尾插入法
LinkList GetElem(LinkList L, int i);  //按序号查找结点值
LinkList LocateElem(LinkList L, ElemType e); //按照结点值查找结点
void Delete_Node(LinkList L, int i); //按序号删除结点
void Print_Node(LinkList L);  //打印单链表
int LinkLength(LinkList L);   //计算结点个数
int ListEmpty(LinkList L);    //判断单链表是否为空
Status ClearList(LinkList L); //清空单链表
Status DestroyList(LinkList L);  //销毁单链表

LinkList.c

#include "LinkList.h"Status InitLinkList(LinkList L) //初始化单链表
{L = (LinkList)malloc(sizeof(LNode));L->next = NULL;return VETERY;
}
LinkList List_HeadInsert(LinkList L)//头插入法
{LNode *new;int i = 10;for (i = 0; i < 10; i++){new = (LNode*)malloc(sizeof(LNode));new->data = i;new->next = L->next;L->next = new;}return L;
}
LinkList List_TailInsert(LinkList L) //尾插入法
{LNode* new;LNode* tail = L;for (int i = 0; i < 10; i++){new = (LinkList)malloc(sizeof(LNode));new->data = i;tail->next = new;tail = new;}tail->next = NULL;return L;
}
LinkList GetElem(LinkList L, int i)    //按序号查找结点值
{int j = 1;LNode* p = L->next;if (i == 0)return L;if (i < 1)return NULL;while (p && j < i){p = p->next;j++;}return p;
}
LinkList LocateElem(LinkList L, ElemType e) //按照结点值查找结点
{LNode* p = L->next;while (p->data != e && p != NULL){p = p->next;}return p;
}
void Delete_Node(LinkList L, int i) //按序号删除结点
{LNode* p = GetElem(L, i - 1);LNode* q;q = p->next;p->next = q->next;free(q);
}
void Print_Node(LinkList L)   //打印单链表
{while (L->next != NULL){L = L->next;printf("%d ", L->data);}printf("\n");
}
int LinkLength(LinkList L)   //求单链表长度
{int k = 0;while (L->next != NULL){k++;L = L->next;  }return k;
}
int ListEmpty(LinkList L)    //判断单链表是否为空
{if (L->next){printf("链表不为空!\n");return FALSE;}else{printf("链表为空!\n");return TRUE;}
}
Status ClearList(LinkList L) //清空单链表
{LinkList p, q;p = L->next;while (p){q = p->next;free(p);p = q;}L->next = NULL;return VETERY;
}
Status DestroyList(LinkList L)  //销毁单链表
{LinkList p;while (L){p = L;L = L->next;free(p);}return VETERY;
}

运行截图:

注:当前为单链表的相关操作以及代码展示,链表除了单链表,还有双链表和循环链表,后续学习完再进行学习笔记的编写!

数据结构(四)—— 线性表的链式存储相关推荐

  1. Python 数据结构 之 线性表 的链式存储结构

    用Python 来实现 C语言中 线性表的链式存储结构. 文章转载请注明:  Python 数据结构 之 线性表 的链式存储结构 代码地址 https://github.com/WenkeZhou/P ...

  2. 【数据结构】线性表的链式存储-单链表

    单链表的定义 线性表的链式存储又称为单链表,它是指通过一组任意的存储单元来存储线性表中的数据元素. 为了建立起数据元素之间的线性关系,对每个链表结点,除了存放元素自身的信息之外,还需要存放一个指向其后 ...

  3. 数据结构之线性表的链式存储实现(附完整代码)

    顺序表插入.删除时需要通过移动数据来实现,影响了执行效率. 而链表不要求逻辑上相邻的两个数据元素物理上也相邻,因此对线性表的插入.删除不需要移动数据元素,只需要修改链. 下面介绍带头结点的链式表: 数 ...

  4. 用Java描述数据结构之线性表的链式存储(链表),模拟LinkedList实现

    上一篇介绍了顺序表:用Java描述数据结构之线性表的顺序存储(顺序表),ArrayList及其方法的介绍 上一篇博客中说明了什么是线性表--线性表就是一个个数据元素逻辑上以一对一的相邻关系(但是在物理 ...

  5. 【数据结构】线性表的链式存储-双链表

    引言 单链表结点中只有一个指向其后继的指针,这使得单链表只能从头结点依次顺序地向后遍历.若要访问某个结点的前驱结点(插入.删除操作时),只能从头开始遍历 ,访问后继结点的时间复杂度为 0(1),访问前 ...

  6. 数据结构(四) -- C语言版 -- 线性表的链式存储 - 循环链表

    文章目录 零.读前说明 一.循环链表的概述 二.循环链表的模型 2.1.包含头节点模型 2.2.不包含头节点模型 三.工程结构及简单测试案例 3.1.测试工程的目录结构 3.2.循环链表示例源码 3. ...

  7. 《数据结构》c语言版学习笔记——单链表结构(线性表的链式存储结构Part1)

    线性表的链式存储结构 数据结构系列文章 第二章 单链表结构 文章目录 线性表的链式存储结构 前言 一.单链表的建立 代码 二.单链表的读取 代码 三.单链表的插入 代码 四.单链表的删除 代码 五.单 ...

  8. 【数据结构】CH2 线性表的链式存储结构

    目录 一.链表概述 1.相关定义 二.单链表 1.插入和删除节点的操作 (1)插入结点 (2)删除结点 2.建立单链表 (1)头插法 (2)尾插法 3.线性表基本运算在单链表中的实现 (1)初始化线性 ...

  9. 数据结构和算法:(3)3.2线性表的链式存储结构

    线性表的链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素也就是说你这个可以放在A地点,这个可以放在E地点,A地点和E地点中间可以隔开一个C地点和D地点,这样是允许的),这组存储单元可以存在 ...

最新文章

  1. 资源 | 斯坦福最新NLP课程上线,选择PyTorch放弃TensorFlow
  2. 使用xilinx的documentation navigator快速查找资料
  3. 剑指Offer #13 调整数组顺序使奇数位于偶数前面 | 图文详解
  4. 关于Server Tomcat v8.0 Server at localhost failed to start的解决办法
  5. Maven 3-Maven依赖版本冲突的分析及解决小结
  6. Nginx的rewrite内容介绍
  7. 久违的反省,容忍现在的自己
  8. KD_Tree 算法
  9. php解析url的三种方法举例
  10. cuSPARSE库:(十二)cusparseDestroyMatDescr()
  11. 源代码src修改为本地图片_20 行 Python 代码批量抓取免费高清图片!
  12. lol游戏挂机软件_“本不想挂机,但系统不让重连!”LOL成“挂机联盟”,5局3挂机...
  13. Ghostexp.exe
  14. Gartner:2018年前沿技术预测
  15. 90后薪资5年翻10倍,靠的不是钢琴手,而是真家伙!
  16. 计算机应用基础教程在线阅读,计算机应用基础教程
  17. 事后诸葛亮(葫芦娃队)
  18. NO.4 项目无法一键打包?自己写个shell脚本吧
  19. cgb2107-day17
  20. css如何让文本框中的输入的文字始终垂直居中

热门文章

  1. Android : CardView的优雅使用
  2. 毕业设计Spring boot问题记录(后端三):java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax;
  3. 科学计算库学习笔记(持续更新)
  4. 传统语音识别介绍【一】—— 前端处理
  5. TC118S/TC118H单通道直流马达驱动IC
  6. sql分组 会计分录_oracle\EBS\常用表\视图\会计分录\mtl_serial_numbers\总账
  7. 多元函数链导法则的理解
  8. 论文笔记:基于并行注意力 UNet的裂缝检测方法
  9. garch dcc用matlab,用matlab工具箱怎么对garch模型做...
  10. NSFC 国家自然科学基金查询