线性表的链式表示——单链表
单链表
定义
线性表的链式存储又称单链表,它是指通过一组任意的存储单元来存储线性表中的数据元素。每个链表的结点,除存放元素自身的信息之外,还需要存放一个指向其后继结点的指针。即单链表的结构分为两部分,其中data为数据域,用来存放元素;next为指针域,用来存放其后继结点的地址。
单链表中结点类型的描述如下:
typedef struct LNode{ //定义单链表结点类型ElemeType data; //数据域struct LNode *next; //指针域
}LNode,*LinkList;
由于单链表是离散地分布在存储空间中,所以单链表是非随机存储的存储结构。
通常用头指针来标识一个单链表,如单链表L,头指针为NULL时表示为一个空表;另外,为了操作方便,会在单链表第一个结点前加一个结点,成为头结点,头结点的指针域指向线性表的第一个元素结点。
单链表的逻辑结构图如下:
单链表上基本操作
单链表是用户不断申请存储单元和改变链接关系而得到的一种特殊数据结构,将链表的左边称为链头,右边称为链尾。
1、头插法建立单链表
该方法从一个空表开始,生成新的结点,并将读取到的数据结构存放到新结点的数据域中,然后将新的结点插入到当前链表的表头,即将链表右端看成固定的,链表不断向左延伸而得到的。头插法最先得到的是尾结点。
头插法建立单链表的算法如下:
LinkList List_HeadInsert(LinkList &L){ //逆向建立单链表LNode *s;int x;L = (LinkList)malloc(sizeof(LNode)); //创建头结点L->next = NULL; //初始化链表为空scanf("%d",&x); //输入结点的值while(x! = 9999 ){ //输入9999表示结束s = (LNode*)malloc(sizeof(LNode)); //创建新结点s->data = x; s->next = L->next;L->next = s; //将新结点插入表中,L为头指针scanf("%d",&x);}return L;}
采用头插法建立单链表时,读入数据的顺序与生成的链表中的元素的顺序是相反。其中每个结点插入的时间为O(1),设单链表长为n,则总时间的复杂度为O(n);
2、尾插法建立单链表
该方法将新结点插入到当前链表的表尾,为此必须增加一个尾指针r,使其始终指向当前链表的尾结点。即将链表的左端固定,链表不断向右延伸。尾插法最先得到的是头结点。
尾插法建立单链表的算法如下:
LinkList List_TailInsert(LinkList &L){ //建立正向单链表int x; //设元素类型为整型L = (LinkList)malloc(sizeof(LNode));LNode *s, *r = L; //r为表尾指针scanf("%d",&x); //输入结点的值while("x != 9999 "){ //输入9999表示结束s = (LinkList *)malloc(sizeof(LNode)); //创建头结点s->data = x; r->next = s;r = s; //将新结点插入表中,L为头指针scanf("%d",&x);}r->next = NULL; //尾结点指针置空return L;}
因为附设一个指向表尾结点的指针,故时间复杂度和头插法相同;
3、按序号查找结点的值
在单链表中从第一个结点出发,顺时针 next 域逐个往下搜索,直到找到第 i 个结点为止,否则返回最后一个结点指针域为 NULL。
按序号查找结点值的算法如下:
LNode *GetElem(LinkList L, int i){int j = 1; //计数,初始为1;LNode *p = L->next; //头结点指针赋给p;if(i == 0) //若 i=0,则返回头结点return 1;if(i < 1) //若 i 无效,则返回NULL;return NULL;while(p && j < i){ //从第 1 个结点开始找,查找第 i 个结点p = p->next;j++;}return p; //返回第 i 个结点的指针,若 i 大于表长则返回 NULL;
}
按序号查找操作的时间复杂度为O(n);
4、按值查找表结点
从单链表的第一个结点开始,从后往前依次比较表中各结点数据域的值,若结点数据域的值等于给定 e ,则返回该结点的指针;若整个单链表中没有这样的结点,则返回NULL。
按值查找表结点的算法如下:
LNode *LocateElem(Linklist L, ElemType e){LNode *p = L->next;while(p != NULL && p-> data != e) //从第1个结点开始查找data域为e的结点p = p->next; return p; //找到后返回该结点指针,否则返回NULL
}
按值查找操作的时间复杂度为O(n)。
5、插入结点操作
插入结点操作是将值为 x 的结点新插入到单链表的第 i 个位置上。先检查插入位置的合法性,然后找到插入位置的前驱结点,即第 i -1 个结点,然后在其后插入新结点。其操作过程如图所示。
实现插入结点的代码片段如下:
p = GetElem(L, i-1); //查找插入位置的前驱结点s->next = p->next;p->next = x;
插入结点操作的时间复杂度为O(n)。
另外:前插是指在某结点的前面插入一个新的结点,后插定义刚好和它相反。在单链表的插入算法中,通常使用后插操作。
以上面的算法为例,首先调用GetElem()找到第 i - 1 个结点,即插入结点的前驱结点后,在对其执行后插操作。由此可知,对结点的前插操作均可转化为后插操作,前提是从单链表的头结点开始顺序查找到其前驱结点,时间复杂度为O(n).
此外,可采用另外一种方法将其转化为后插操作来实现,设待插入结点为 * s,将 * s插入到 * p的前面,我们仍然将 * s插入到 * p的后面,然后将 p->data 与 s->data 交换,这样既满足了逻辑关系,又使得时间复杂度为O(1),算法代码片段如下:
s->next = p->next; //修改指针域,不能颠倒p->next = s;temp = p->data; //交换数据域部分p->data = s->data;s->data = temp;
6、删除结点操作
删除结点操作是将单链表的第 i 的结点删除。先检查要删除位置的合法性, 后查找表中第 i-1 个结点,即被删除结点的前驱结点,然后再将其删除。其操作过程如图所示。
实现删除结点的代码片段如下:
p = GetMlem(L, i-1); //查找删除结点的前驱结点q = p->next;p->next = q->next;free(q); //释放结点的存储空间
删除结点操作的时间复杂度为O(n)。
另外:删除结点 * p的操作可通过删除 * p的后继结点来实现,实质就是将其后继结点的值赋予自身,然后删除后继结点,也使得时间复杂度为O(1)。
实现算法部分代码如下:
q = q->next; //令 q 指向*p的后继结点p->data = p->next->data; //和后继结点交换数据域p->next = q->next; //将*p结点从链中断开free(q); //释放后继结点的存储空间
7、求表长的操作
求表长的操作就是计算单链表中数据结点(不含头结点)的个数,需要从第一个结点开始顺序依次访问表中每个结点,为此需要设置一个计数器变量,每访问一个结点,计数器加 1,直到访问空结点为止。算法时间复杂度为O(n)。
【注】单链表的长度是不包括头结点的,因此不带头结点和带头结点的单链表在求表长操作上会略有不同。对不带头结点的单链表,当表为空时,要单独处理。
线性表的链式表示——单链表相关推荐
- 【数据结构】线性表的链式存储-单链表
单链表的定义 线性表的链式存储又称为单链表,它是指通过一组任意的存储单元来存储线性表中的数据元素. 为了建立起数据元素之间的线性关系,对每个链表结点,除了存放元素自身的信息之外,还需要存放一个指向其后 ...
- 线性表的链式实现(单链表)
单链表的定义 为了表示每个数据元素与其直接后续元素之间的逻辑关系,每个元素除了存储本身的信息外,还需要存储指示其直接后续的信息,即每个结点存放本身的数据元素和下一个元素的地址.n个结点连接成一个链式线 ...
- 线性表的链式存储-单链表
单链表操作 [x] 单链表的创建(尾插法.头插法) [x] 单链表的查找操作 [x] 单链表的删除操作 [x] 单链表的逆置操作(使用头插法) [x] 单链表表长的计算 [x] 打印单链表 单链表的创 ...
- 数据结构与算法笔记(三) 线性表(链式描述) 链表
在链式描述中,线性表元素的位置在内存中是随机的,每个元素都有一个明确的指针指向线性表的下一个元素的位置. 1.单向链表: 数据对象的每一个元素都用一个单元或者节点来描述,每个节点都明确包含另一个相关节 ...
- 用Java描述数据结构之线性表的链式存储(链表),模拟LinkedList实现
上一篇介绍了顺序表:用Java描述数据结构之线性表的顺序存储(顺序表),ArrayList及其方法的介绍 上一篇博客中说明了什么是线性表--线性表就是一个个数据元素逻辑上以一对一的相邻关系(但是在物理 ...
- 线性表的链式表示——双链表
定义 双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱.所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点.一般我们都构造双向循 ...
- 【数据结构】线性表的链式存储-双链表
引言 单链表结点中只有一个指向其后继的指针,这使得单链表只能从头结点依次顺序地向后遍历.若要访问某个结点的前驱结点(插入.删除操作时),只能从头开始遍历 ,访问后继结点的时间复杂度为 0(1),访问前 ...
- 数据结构(四) -- C语言版 -- 线性表的链式存储 - 循环链表
文章目录 零.读前说明 一.循环链表的概述 二.循环链表的模型 2.1.包含头节点模型 2.2.不包含头节点模型 三.工程结构及简单测试案例 3.1.测试工程的目录结构 3.2.循环链表示例源码 3. ...
- 什么是线性表?什么是线性表的顺序存储结构?什么是线性表的链式存储结构?
1.线性表是最简单也是最常用的一种数据结构.线性表的例子不胜枚举,例如,英文字母表就是一个线性表,表中的英文字母是一个数据元素. 2.线性表的定义:线性表是具有相同特性的数据元素的一个有限序列. 3. ...
最新文章
- 看漫画学Python,屏幕前的彦祖要不要试试?
- oracle数据库查询人员和岗位,oracle岗位需求收集
- 2021重庆江北中学高考成绩查询,重庆江北中学校2021年排名
- 115 存至网盘 也有限制?
- phonegap在android中如何退出程序
- Android 开源项目android-open-project工具库解析之(一) 依赖注入,图片缓存,网络相关,数据库orm工具包,Android公共库...
- 课时4:改进我们的小游戏
- 开源方案搭建可离线的精美矢量切片地图服务-6.Mapbox之.pbf字体库
- 声学计算机软件,常用声学仿真软件汇总
- wx-微信公众号-静默登陆授权
- LuatOS之LVGL字体篇
- 【数据库查询--电影制片系列】-- 检索出Studio表中制片公司st1的地址。
- 中国电信天翼空间发布移动应用开发运营平台
- 分享一个特别喜欢的背景图片
- 重磅!共掘千亿大数据市场 智领云2021年合作伙伴招募计划正式启动
- 下载最新版本火狐浏览器,并且下载geckodriver.exe
- matplotlib红橙黄绿青蓝紫(含颜色大全)
- android的一些简单配置修改(2)
- 【JavaScript】分享一个定时到网站上签到/签退的JS脚本
- 文献计量之洛特卡定律