数据结构之线性表的链式存储结构(C语言)
文章目录
- 引言
- 线性表链式存储结构定义
- 头指针与头结点的异同
- 线性链表存储结构代码秒描述
- 单链表的读取
- 获得链表第i个数据的算法思路
- 单链表的插入
- 单链表第i个数据插入结点的算法思路
- 单链表的删除
- 单链表第i个数据删除结点的算法思路
- 整体代码
引言
线性表的顺序存储结构,它是有缺点的,最大的缺点就是插入和删除时需要移动大量的数据元素,这显然需要耗费时间。
线性表链式存储结构定义
特点是用一组任意的存储单元存储线性表的数据元素,这组存储单元可以是连续的。也可以是不连续的。这就意味着这些数据元素可以存在内存内存未被占用的任意位置。
在顺序存储结构中,每个数据元素只需要存储数据元素信息就可以了。现在链式结构中,除了要存储数据元素信息之外,还要存储它的后继元素的存储地址。
为了表示每个数据元素ai与其直接后继数据元素ai+1之间的逻辑关系,对元素ai来说,除了存储其本身的信息之外,还需要存储一个指示其直接后继的信息(即直接后继的存储位置)。把存储数据元素信息的域称为数据域,把存储直接后继位置的域称为指针域。指针域汇中存储的信息称作指针或链。这两部分信息组成数据元素ai的存储映像,称为结点(Node)
**n个结点(ai的存储映像)链接成一个链表,即为线性链表(a1,a2,……an,)的链式存储结构,因此链表的每个结点只包含一个指针域, 所以叫单链表。 **
我们把链表第一个结点的存储位置叫做头指针,那么整个链表的存取就必须从头指针进行。之后的每一个结点,其实就是上一个的后继指针指向的位置。最后一个结点指针为空(NULL或^符号表示)。
有时候为了方便对链表进行操作,会在单链表的第一个结点前附设一个结点,称为头结点。头结点的数据域可以不存储任何信息(可以存储线性表的长度等附加信息),头结点的指针域存储指向第一个结点的指针。
头指针与头结点的异同
头指针是只链表指向第一个结点的指针,若链表有头结点,则是指向头结点的指针,头指针具有标识作用,所以常用头指针冠以链表的名字,无论链表是否为空,头指针均不为空。头指针是链表的必要元素。
头结点是为了操作方便的统一和方便而设立的,放在第一元素的结点之前,其数据域一般无意义。有了头结点对在一个元素结点前插入点和删除第一个结点,其操作与其他结点的操作就统一了。头结点不一定是链表必要元素。
线性链表存储结构代码秒描述
若线性表为空,则头结点的指针域为空。
线性表中数据元素及数据元素之间的逻辑关系。
带有头结点的单链表。
空链表。
单链表中,可以用结构指针描述(C语言)
typedef struct Node
{ElemType data;struct Node *next;
}Node;
typedef struct Node *LinkList; /* 定义LinkList */
结点由存放数据元素的数据域存放后继结点地址的指针域组成。
假设p是指向线性表第i个元素的指针,则该节点ai的数据域,我们我们可以用p->data来表示,p->data的值是一个数据元素,结点ai的指针域可用p->next来表示,p->next的值是一个指针。p->next指向第i+1个元素,即指向ai+1的指针。
如果p->data =ai,那么p->next->data=ai+1.
单链表的读取
获得链表第i个数据的算法思路
1、声明一个结点p指向链表第一个结点,初始化j从1开始。
2、当j < i时,就遍历链表,让p的指针向后移动,不断指向下一个结点,j累加1。
3、若链表末尾p为空,则说明第i个元素不存在。
4、否则查找成功,返回结点p的数据。
/* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) */
/* 操作结果:用e返回L中第i个数据元素的值 */
Status GetElem(LinkList L,int i,ElemType *e)
{int j;LinkList p; /* 声明一结点p */p = L->next; /* 让p指向链表L的第一个结点 */j = 1; /* j为计数器 */while (p && j<i) /* p不为空或者计数器j还没有等于i时,循环继续 */{ p = p->next; /* 让p指向下一个结点 */++j;}if ( !p || j>i ) return ERROR; /* 第i个元素不存在 */*e = p->data; /* 取第i个元素的数据 */return OK;
}
从头开始找,直到第i个元素为止,由于这个算法的时间复杂度取决于i的位置,i=1时,则不需要遍历,第一个就取出数据了,i=n时,需要遍历n-1次才可以,因此最坏情况下的时间复杂度为O(n)。
单链表的插入
假设存储元素e的结点为s,要实现结点p,p->next和s之间逻辑关系的变化,只需要将结点s插入到结点p和p->next之间即可,问题是怎么插入?
不需要一定其他的结点,只需要让s->next和p->next的指针做一点改变即可。
s->next = p->next;
p->next = s;
就是让p的后继结点改成s的后继结点,再把结点s变成p的后继结点。
考虑一下这两句的顺序的可不可以交换?
如果先p->next = s;在s->next = p->next;会怎么样?
此时的第一句使得p->next给覆盖成s的地址了。那么s->next=p->next,其实就等于s->next =s.
插入结点s后,链表如图
对于单链表的表头和表尾的特殊情况,操作是想相同的。
单链表第i个数据插入结点的算法思路
1、声明一结点p指向链表第一个结点,初始化j从1开始。
2、当j<i时,就遍历链表,让p的指针向后移动,不断指向下一个结点,j累加1。
3、若到链表末尾p为空,则说明第i个元素不存在。
4、否则查找成功,在系统生成一个空结点s。
5、将数据元素e赋值给s->data。
6、单链表的插入标准语句s->next = p->next;p->next = s;
7、返回成功。
/* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L), */
/* 操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1 */
Status ListInsert(LinkList *L,int i,ElemType e)
{ int j;LinkList p,s;p = *L; j = 1;while (p && j < i) /* 寻找第i个结点 */{p = p->next;++j;} if (!p || j > i) return ERROR; /* 第i个元素不存在 */s = (LinkList)malloc(sizeof(Node)); /* 生成新结点(C语言标准函数) */s->data = e; s->next = p->next; /* 将p的后继结点赋值给s的后继 */p->next = s; /* 将s赋值给p的后继 */return OK;
}
malloc函数作用就是生成一个新的结点,其类型与node是一样的。实质就是在内存中找了一小块空地,准备用来存放e数据s的结点。
单链表的删除
假设存储ai的结点q,要实现将结点q删除单链表的操作。其实就是将它的前驱结点的指针绕过,指向它的后继结点即可。
我们需要做的就是一步
p->next=p->next->next,用q来取代p->next
q = p->next;
p->next = q->next;
就是让p的后继的后继结点改成p的后继结点。
单链表第i个数据删除结点的算法思路
1、声明一结点p指向链表第一个结点,初始化j从1开始。
2、当j<i时,就遍历链表,就让p的指针向后移动,不断指向下一个结点,j累加1。
3、若到链表末尾p为空,则说明第i个元素不存在。
4、否则查找成功,将想删除的结点p->next赋值给q。
5、单链表的删除语句q = p->next;p->next = q->next。
6、将q结点中的数据赋值给e,作为返回。
7、释放q结点。
8、返回成功。
/* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) */
/* 操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1 */
Status ListDelete(LinkList *L,int i,ElemType *e)
{ int j;LinkList p,q;p = *L;j = 1;while (p->next && j < i) /* 遍历寻找第i个元素 */{p = p->next;++j;}if (!(p->next) || j > i) return ERROR; /* 第i个元素不存在 */q = p->next;p->next = q->next; /* 将q的后继赋值给p的后继 */*e = q->data; /* 将q结点中的数据给e */free(q); /* 让系统回收此结点,释放内存 */return OK;
}
函数free作用就是让嗅探回收一个Node结点,释放内存。
整体代码
#include "stdio.h"
#include "string.h"
#include "ctype.h"
#include "stdlib.h"
#include "io.h"
#include "math.h"
#include "time.h"#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0#define MAXSIZE 20 /* 存储空间初始分配量 */typedef int Status;/* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int ElemType;/* ElemType类型根据实际情况而定,这里假设为int */Status visit(ElemType c)
{printf("%d ",c);return OK;
}typedef struct Node
{ElemType data;struct Node *next;
}Node;
typedef struct Node *LinkList; /* 定义LinkList *//* 初始化顺序线性表 */
Status InitList(LinkList *L)
{ *L=(LinkList)malloc(sizeof(Node)); /* 产生头结点,并使L指向此头结点 */if(!(*L)) /* 存储分配失败 */return ERROR;(*L)->next=NULL; /* 指针域为空 */return OK;
}/* 初始条件:顺序线性表L已存在。操作结果:若L为空表,则返回TRUE,否则返回FALSE */
Status ListEmpty(LinkList L)
{ if(L->next)return FALSE;elsereturn TRUE;
}/* 初始条件:顺序线性表L已存在。操作结果:将L重置为空表 */
Status ClearList(LinkList *L)
{ LinkList p,q;p=(*L)->next; /* p指向第一个结点 */while(p) /* 没到表尾 */{q=p->next;free(p);p=q;}(*L)->next=NULL; /* 头结点指针域为空 */return OK;
}/* 初始条件:顺序线性表L已存在。操作结果:返回L中数据元素个数 */
int ListLength(LinkList L)
{int i=0;LinkList p=L->next; /* p指向第一个结点 */while(p) {i++;p=p->next;}return i;
}/* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) */
/* 操作结果:用e返回L中第i个数据元素的值 */
Status GetElem(LinkList L,int i,ElemType *e)
{int j;LinkList p; /* 声明一结点p */p = L->next; /* 让p指向链表L的第一个结点 */j = 1; /* j为计数器 */while (p && j<i) /* p不为空或者计数器j还没有等于i时,循环继续 */{ p = p->next; /* 让p指向下一个结点 */++j;}if ( !p || j>i ) return ERROR; /* 第i个元素不存在 */*e = p->data; /* 取第i个元素的数据 */return OK;
}/* 初始条件:顺序线性表L已存在 */
/* 操作结果:返回L中第1个与e满足关系的数据元素的位序。 */
/* 若这样的数据元素不存在,则返回值为0 */
int LocateElem(LinkList L,ElemType e)
{int i=0;LinkList p=L->next;while(p){i++;if(p->data==e) /* 找到这样的数据元素 */return i;p=p->next;}return 0;
}/* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L), */
/* 操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1 */
Status ListInsert(LinkList *L,int i,ElemType e)
{ int j;LinkList p,s;p = *L; j = 1;while (p && j < i) /* 寻找第i个结点 */{p = p->next;++j;} if (!p || j > i) return ERROR; /* 第i个元素不存在 */s = (LinkList)malloc(sizeof(Node)); /* 生成新结点(C语言标准函数) */s->data = e; s->next = p->next; /* 将p的后继结点赋值给s的后继 */p->next = s; /* 将s赋值给p的后继 */return OK;
}/* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) */
/* 操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1 */
Status ListDelete(LinkList *L,int i,ElemType *e)
{ int j;LinkList p,q;p = *L;j = 1;while (p->next && j < i) /* 遍历寻找第i个元素 */{p = p->next;++j;}if (!(p->next) || j > i) return ERROR; /* 第i个元素不存在 */q = p->next;p->next = q->next; /* 将q的后继赋值给p的后继 */*e = q->data; /* 将q结点中的数据给e */free(q); /* 让系统回收此结点,释放内存 */return OK;
}/* 初始条件:顺序线性表L已存在 */
/* 操作结果:依次对L的每个数据元素输出 */
Status ListTraverse(LinkList L)
{LinkList p=L->next;while(p){visit(p->data);p=p->next;}printf("\n");return OK;
}/* 随机产生n个元素的值,建立带表头结点的单链线性表L(头插法) */
void CreateListHead(LinkList *L, int n)
{LinkList p;int i;srand(time(0)); /* 初始化随机数种子 */*L = (LinkList)malloc(sizeof(Node));(*L)->next = NULL; /* 先建立一个带头结点的单链表 */for (i=0; i<n; i++) {p = (LinkList)malloc(sizeof(Node)); /* 生成新结点 */p->data = rand()%100+1; /* 随机生成100以内的数字 */p->next = (*L)->next; (*L)->next = p; /* 插入到表头 */}
}/* 随机产生n个元素的值,建立带表头结点的单链线性表L(尾插法) */
void CreateListTail(LinkList *L, int n)
{LinkList p,r;int i;srand(time(0)); /* 初始化随机数种子 */*L = (LinkList)malloc(sizeof(Node)); /* L为整个线性表 */r=*L; /* r为指向尾部的结点 */for (i=0; i<n; i++) {p = (Node *)malloc(sizeof(Node)); /* 生成新结点 */p->data = rand()%100+1; /* 随机生成100以内的数字 */r->next=p; /* 将表尾终端结点的指针指向新结点 */r = p; /* 将当前的新结点定义为表尾终端结点 */}r->next = NULL; /* 表示当前链表结束 */
}int main()
{ LinkList L;ElemType e;Status i;int j,k;i=InitList(&L);printf("初始化L后:ListLength(L)=%d\n",ListLength(L));for(j=1;j<=5;j++)i=ListInsert(&L,1,j);printf("在L的表头依次插入1~5后:L.data=");ListTraverse(L); printf("ListLength(L)=%d \n",ListLength(L));i=ListEmpty(L);printf("L是否空:i=%d(1:是 0:否)\n",i);i=ClearList(&L);printf("清空L后:ListLength(L)=%d\n",ListLength(L));i=ListEmpty(L);printf("L是否空:i=%d(1:是 0:否)\n",i);for(j=1;j<=10;j++)ListInsert(&L,j,j);printf("在L的表尾依次插入1~10后:L.data=");ListTraverse(L); printf("ListLength(L)=%d \n",ListLength(L));ListInsert(&L,1,0);printf("在L的表头插入0后:L.data=");ListTraverse(L); printf("ListLength(L)=%d \n",ListLength(L));GetElem(L,5,&e);printf("第5个元素的值为:%d\n",e);for(j=3;j<=4;j++){k=LocateElem(L,j);if(k)printf("第%d个元素的值为%d\n",k,j);elseprintf("没有值为%d的元素\n",j);}k=ListLength(L); /* k为表长 */for(j=k+1;j>=k;j--){i=ListDelete(&L,j,&e); /* 删除第j个数据 */if(i==ERROR)printf("删除第%d个数据失败\n",j);elseprintf("删除第%d个的元素值为:%d\n",j,e);}printf("依次输出L的元素:");ListTraverse(L); j=5;ListDelete(&L,j,&e); /* 删除第5个数据 */printf("删除第%d个的元素值为:%d\n",j,e);printf("依次输出L的元素:");ListTraverse(L); i=ClearList(&L);printf("\n清空L后:ListLength(L)=%d\n",ListLength(L));CreateListHead(&L,20);printf("整体创建L的元素(头插法):");ListTraverse(L); i=ClearList(&L);printf("\n删除L后:ListLength(L)=%d\n",ListLength(L));CreateListTail(&L,20);printf("整体创建L的元素(尾插法):");ListTraverse(L); return 0;
}
数据结构之线性表的链式存储结构(C语言)相关推荐
- Python 数据结构 之 线性表 的链式存储结构
用Python 来实现 C语言中 线性表的链式存储结构. 文章转载请注明: Python 数据结构 之 线性表 的链式存储结构 代码地址 https://github.com/WenkeZhou/P ...
- 数据结构和算法:(3)3.2线性表的链式存储结构
线性表的链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素也就是说你这个可以放在A地点,这个可以放在E地点,A地点和E地点中间可以隔开一个C地点和D地点,这样是允许的),这组存储单元可以存在 ...
- 从零开始学数据结构和算法(二)线性表的链式存储结构
链表 链式存储结构 定义 线性表的链式存储结构的特点是用一组任意的存储单元的存储线性表的数据元素,这组存储单元是可以连续的,也可以是不连续的. 种类 结构图 单链表 应用:MessageQueue 插 ...
- 《数据结构》c语言版学习笔记——其他链表(线性表的链式存储结构Part2)
线性表的链式存储结构 数据结构系列文章 第三章 循环链表.双向链表 文章目录 线性表的链式存储结构 前言 一.循环链表 (一)定义 (二)尾指针 二.双向链表 (一)定义 (二)代码 总结 前言 提示 ...
- 《数据结构》c语言版学习笔记——单链表结构(线性表的链式存储结构Part1)
线性表的链式存储结构 数据结构系列文章 第二章 单链表结构 文章目录 线性表的链式存储结构 前言 一.单链表的建立 代码 二.单链表的读取 代码 三.单链表的插入 代码 四.单链表的删除 代码 五.单 ...
- 【数据结构】CH2 线性表的链式存储结构
目录 一.链表概述 1.相关定义 二.单链表 1.插入和删除节点的操作 (1)插入结点 (2)删除结点 2.建立单链表 (1)头插法 (2)尾插法 3.线性表基本运算在单链表中的实现 (1)初始化线性 ...
- 数据结构-线性表(链式存储结构)
线性表(链式存储结构) 特点: 用一组任意的存储单元存储线性表的数据结构,这组存储单元可以是连续的,也可以是不连续的. 对数据结构ai来说,除了存储其本身的信息之外,还需存储一个指示其后继的信息(即直 ...
- 数据结构开发(5):线性表的链式存储结构
0.目录 1.线性表的链式存储结构 2.单链表的具体实现 3.顺序表和单链表的对比分析 4.小结 1.线性表的链式存储结构 顺序存储结构线性表的最大问题是: 插入和删除需要移动大量的元素!如何解决? ...
- 链表list(链式存储结构实现)_5 线性表的链式存储结构
系列文章参考资料为<大话数据结构>,源码为个人私有,未经允许不得转载 线性表的链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素,可以使连续的,也可以不连续,也就意味这些元素可以 ...
- 什么是线性表?什么是线性表的顺序存储结构?什么是线性表的链式存储结构?
1.线性表是最简单也是最常用的一种数据结构.线性表的例子不胜枚举,例如,英文字母表就是一个线性表,表中的英文字母是一个数据元素. 2.线性表的定义:线性表是具有相同特性的数据元素的一个有限序列. 3. ...
最新文章
- js中的装饰器执行顺序
- java tomcat日志中文乱码问题解决
- 提高开发效率之安卓模板(上面有四种模板的教程,我之前会两种,看完之后还是只会两种2333)
- Keras + Ubuntu环境搭建
- 第九届蓝桥杯 Java B组 第三题 复数幂 (详解)
- 京东白条要上征信了!你用还是不用
- Ant Build.xml
- curl有php内存缓存,PHP CURL内存泄露的解决方法
- Linux之lastb命令
- VMwareESX上的SCOM控制台无法正常运行
- Flutter技术在会展云中大显身手
- Linux文件管理 | Liunx 常用命令
- 冰蝎shell_冰蝎全系列有效:针对 HTTPS 加密流量的 webshell 检测研究
- 古琴调音频率及音位图(正调F调)
- 【ML】 第四章 训练模型
- 关于java多态性之父类引用指向子类对象
- Eclipse中出现“polling news feeds”的解决办法
- 数据结构与算法(python) 线性结构:无序列表 Unordered List以及链表
- 解决一个输出文档的问题
- Get和Post的区别是什么?
热门文章
- java开发中的dorado_dorado7开发常用技巧及代码
- 面经整理:大华C++服务器开发(2021-07-19)
- 北京航空航天大学计算机考研信息汇总
- linux socket监听端口,Linux-socket使用
- 逻辑回归算法原理MATLAB,逻辑回归算法(MATLAB)
- GIS数据转换器(栅格)(栅格向矢量网格和栅格向栅格的转换)
- 2022年信息安全工程师考试知识点:信息系统安全测评
- 测试计划报告---5W1H
- js中如何解决跨域问题
- IDEA打包jar-解决错误: 找不到或无法加载主类 main