【数据结构】--章节2.3----线性表的链式表示和实现
文章目录
- 注意:
- 存储结构
- 链表的初始化
- 在链表末尾添加元素
- 遍历链表
- 删除链表中指定位置元素
- 删除链表中值为e的元素
- 在第i个元素前面插入元素
- 循环链表
- 双向链表
- 使用链表实现一元多项式的加法运算
注意:
这章需要注意一下,整个章节是这样的
- 第28页,给出了一个单链表存储结构
- 第29页到第31页:介绍了单链表的四个算法—2.8查找,2.9插入,2.10删除,2.11创建,2.12归并
- 第31页给出了静态单链表的存储结构
- 第32到34页:介绍了静态单链表的五个算法:2.13查找、2.14初始化、2.15malloc、2.16free、2.17一个例子
- 第35页,介绍了循环链表
- 第35页,介绍了双向链表存储结构
- 第36页,算法2.18介绍了双向循环链表的插入算法,2.19介绍了删除算法
- 最终在第37页,重新给出了线性链表的类型定义以及重写了两个算法,2.20插入、2.21归并
- 39页到43页展示了如何用线性表表示一元多项式以及计算一元多项式
所以,其实这章是个讨论的过程,最初我们给出一个链表的定义,并且实践了他的各种操作,我们会发现这个结构存在一些弊端,在做不同行为的时候,操作不统一,为了统一操作,最终我们根据需求,重新定义了链表的存储结构,统一了各项操作。
本文内容与课本此章节的顺序并不一一对应。
存储结构
最终的这个存储结构采用头指针---->头结点---->数据头结点---->数据结点的模式
typedef struct LDataNode{int x;struct LDataNode *next;//结点指针
}LDataNode;
typedef struct LHeadNode{LDataNode *next; //头LDataNode *tail; //尾
}LHeadNode,*LinkList;
很多同学不太理解,为什么这个存储结构要一遍一遍的改,很多个版本,其实是为了统一化操作。
如果你也不清楚什么叫统一操作,那我举个例子。
我们最终采用的这个结构,你会发现,头结点由于存放关于链表的信息,所以不存放数据,但是还有一个数据节点也没存东西,为什么不存呢,岂不是浪费了?这里就体现了统一操作的思想。
你想一下,如果第一个位置也存放数据,那么当链表为空的时候,头尾指针没地方指,所以要置为空,当来了第一个元素,链表不为空了,那头尾指针都要指向这个元素,而来了第二个元素的时候,我们只需要更新尾指针就好了,不用更新头指针了。发现了没,同样是添加元素,第一个和其他元素,操作不一样,也就意味着我们要特殊判断,而我们牺牲第一个结点,不存数据,就把操作统一化了,你就会发现,不论添加第几个元素,操作都一样。
不止这一个例子,还有别的类似的操作,总之就记住,我们优化存储结构,都是为了操作统一化。
ps:当然了,并没有规定你写链表必须写这样的结构。如果你完全理解了这几个结构的优缺点,你就可以灵活运用了,根据实际情况,选择最合适的结构。
下面我写的算法,都基于本文给出的存储结构来写!,(而非第28页的存储结构)
链表的初始化
int InitList(LinkList *head){//初始化链表*head =(LinkList)malloc(sizeof(LHeadNode));LDataNode *vhNode =(LDataNode *)malloc(sizeof(LDataNode));if(*head==NULL||vhNode==NULL){return -1;//申请失败}vhNode->next=NULL;(*head)->next=vhNode;(*head)->tail=vhNode;return 0;
}
在链表末尾添加元素
由于头结点的指针域中有了尾指针,所以使我们在链表末尾添加元素变得很容易。
int AddList(LinkList *head,int a){//链表末尾添加元素LDataNode *vhNode =(LDataNode *)malloc(sizeof(LDataNode));if(vhNode==NULL){return -1;//申请失败}vhNode->x=a;vhNode->next=NULL;(*head)->tail->next=vhNode;//挂到末尾(*head)->tail=vhNode;//尾指针更新return 0;
}
遍历链表
注意,遍历的时候我们传的参数,与其他操作不一样,由于遍历只用访问元素,不需要对结点做出改变,所以不需要双重指针!单重指针就够了。
int LTraverse(LinkList head){//遍历输出链表LDataNode *p=head->next->next;//注意 第一个数据节点不存数据 所以 第二个才是开头while(p){printf("%d ",p->x);p=p->next;}printf("\n");return 0;
}
删除链表中指定位置元素
/******
author: 1900
language: C
******/
#include<stdio.h>
#include<string.h>
#include<malloc.h>
typedef struct LDataNode{int x;struct LDataNode *next;
}LDataNode;
typedef struct LHeadNode{LDataNode *next;LDataNode *tail;
}LHeadNode,*LinkList;int InitList(LinkList *head){//初始化链表*head =(LinkList)malloc(sizeof(LHeadNode));LDataNode *vhNode =(LDataNode *)malloc(sizeof(LDataNode));if(*head==NULL||vhNode==NULL){return -1;//申请失败}vhNode->next=NULL;(*head)->next=vhNode;(*head)->tail=vhNode;return 0;
}
int AddList(LinkList *head,int a){//链表末尾添加元素LDataNode *vhNode =(LDataNode *)malloc(sizeof(LDataNode));if(vhNode==NULL){return -1;//申请失败}vhNode->x=a;vhNode->next=NULL;(*head)->tail->next=vhNode;//挂到末尾(*head)->tail=vhNode;//尾指针更新return 0;
}
int LTraverse(LinkList head){//遍历输出链表LDataNode *p=head->next->next;while(p){printf("%d ",p->x);p=p->next;}printf("\n");return 0;
}
//删除链表中第i个元素
int DeleteList(LinkList *head,int i,int *e){if(i<1) return -1;//不合法int num=1;LDataNode *p=(*head)->next;//指向第0个结点 现在p->next就是第一个while(num!=i&&p->next!=NULL){p=p->next;num++;}if(p->next==NULL)return -1;//不合法*e=p->next->x;p->next=p->next->next;//删除return 0;
}
int main(){LinkList head;//初始化一个链表InitList(&head);//赋值为1-10for(int i=1;i<=10;i++){AddList(&head,i);}LTraverse(head);//遍历链表int e;DeleteList(&head,3,&e);//删除第三个元素printf("%d\n",e);//打印被删除的元素LTraverse(head);//遍历删除后的链表
}
删除链表中值为e的元素
与删除第i个元素类似,循环条件改改就行了。
无论是删除值为e的元素还是删除第i个元素,首先就是要找到它,所以,关于在链表中查找元素的算法,我就不单独写了。
//删除链表中值为e的元素
int DeletList_value(LinkList *head,int e){LDataNode *p=(*head)->next;//指向第0个结点 现在p->next就是第一个while(p->next->x!=e){p=p->next;}if(p->next==NULL)return -1;//没找到p->next=p->next->next;//删除return 0;
}
在第i个元素前面插入元素
注意,这里的在第i个元素前面插入元素e,不包括在末尾插入元素的操作。
//第i个元素前面插入元素e
int InsertList(LinkList *head,int i,int e){if(i<1) return -1;//不合法int num=1;LDataNode *p=(*head)->next;//指向第0个结点 现在p->next就是第一个while(num!=i&&p->next!=NULL){p=p->next;num++;}if(p->next==NULL)return -1;//不合法LDataNode *vhNode =(LDataNode *)malloc(sizeof(LDataNode));if(vhNode==NULL){return -1;//申请失败}vhNode->x=e;vhNode->next=p->next;p->next=vhNode;return 0;
}
循环链表
写法不唯一!
循环链表与普通链表的区别只是最后一个元素指向头结点,在判断结束的时候判断是否指向头结点。
这里给出其初始化函数。
int InitList(LinkList *head){*head =(LinkList)malloc(sizeof(LHeadNode));LDataNode *vhNode =(LDataNode *)malloc(sizeof(LDataNode));if(*head==NULL||vhNode==NULL){return -1;}vhNode->next=(*head)->next;//最后一个元素指向开头(*head)->next=vhNode;(*head)->tail=vhNode;return 0;
}
双向链表
双向链表与单链表相比,就是增加了前驱指针,方便向前寻找前驱。
下面这个例子,实现了通过前驱指针倒着遍历整个链表
/******
author: 1900
language: C
******/
#include<stdio.h>
#include<string.h>
#include<malloc.h>
typedef struct LDataNode{int x;struct LDataNode *next;struct LDataNode *pre;
}LDataNode;
typedef struct LHeadNode{LDataNode *next;LDataNode *tail;
}LHeadNode,*LinkList;
int InitList(LinkList *head){*head =(LinkList)malloc(sizeof(LHeadNode));LDataNode *vhNode =(LDataNode *)malloc(sizeof(LDataNode));if(*head==NULL||vhNode==NULL){return -1;}vhNode->next=NULL;vhNode->pre=NULL;//首结点前驱为空 也可以置为他自己 根据需要来设置(*head)->next=vhNode;(*head)->tail=vhNode;return 0;
}
int AddList(LinkList *head,int a){//链表末尾添加元素LDataNode *vhNode =(LDataNode *)malloc(sizeof(LDataNode));if(vhNode==NULL){return -1;}vhNode->x=a;vhNode->next=NULL;vhNode->pre=(*head)->tail;//设置前驱(*head)->tail->next=vhNode;(*head)->tail=vhNode;return 0;
}
int d_LTraverse(LinkList head){//利用前驱指针遍历链表LDataNode *p=head->tail;while(p->pre!=NULL){printf("%d ",p->x);p=p->pre;}printf("\n");return 0;
}
int LTraverse(LinkList head){//遍历输出链表LDataNode *p=head->next->next;while(p){printf("%d ",p->x);p=p->next;}printf("\n");return 0;
}
int main(){LinkList head;//初始化一个链表InitList(&head);//赋值为1-10for(int i=1;i<=10;i++){AddList(&head,i);}LTraverse(head);//遍历链表d_LTraverse(head);//利用前驱指针 遍历链表
}
使用链表实现一元多项式的加法运算
我们用链表表示多项式的时候,是存下每一项的系数和指数
做加法时候,指数相同,直接把系数相加就好了,指数不同不能加。
下面给一个实现例子。
用链表实现这两个多项式相加。
/******
author: 1900
language: C
需求:两个一元多项式做加法
要求:每个多项式有五项,输入每一项的系数和指数,且按照指数升序输入,最终打印相加后的多项式各项系数和指数。
******/
#include<stdio.h>
#include<string.h>
#include<malloc.h>
typedef struct LDataNode{int x;//系数int z;//指数struct LDataNode *next;
}LDataNode;
typedef struct LHeadNode{LDataNode *next;LDataNode *tail;
}LHeadNode,*LinkList;int InitList(LinkList *head){//初始化链表*head =(LinkList)malloc(sizeof(LHeadNode));LDataNode *vhNode =(LDataNode *)malloc(sizeof(LDataNode));if(*head==NULL||vhNode==NULL){return -1;//申请失败}vhNode->next=NULL;(*head)->next=vhNode;(*head)->tail=vhNode;return 0;
}
int AddList(LinkList *head,int a1,int a2){//链表末尾添加元素LDataNode *vhNode =(LDataNode *)malloc(sizeof(LDataNode));if(vhNode==NULL){return -1;}vhNode->x=a1;//系数vhNode->z=a2;//指数vhNode->next=NULL;(*head)->tail->next=vhNode;(*head)->tail=vhNode;return 0;
}
int LTraverse(LinkList head){LDataNode *p=head->next->next;while(p){printf("系数:%d 指数: %d\n",p->x,p->z);p=p->next;}printf("\n");return 0;
}
Add_LinkList(LinkList *H1,LinkList *H2,LinkList *H3){LDataNode *p=(*H1)->next->next;LDataNode *q=(*H2)->next->next;while(q&&p){if(q->z==p->z){AddList(H3,q->x+p->x,q->z);q=q->next;p=p->next;}else if(q->z>p->z){AddList(H3,q->x,q->z);q=q->next;}else if(q->z<p->z){AddList(H3,p->x,p->z);p=p->next;}}while(q){AddList(H3,q->x,q->z);q=q->next;}while(p){AddList(H3,p->x,p->z);p=p->next;}return 0;
}int main(){LinkList H1,H2,H3;InitList(&H1);InitList(&H2);InitList(&H3);int T1=5,T2=5,a1,a2;while(T1--){ //输入第一个多项式scanf("%d%d",&a1,&a2);AddList(&H1,a1,a2);}while(T2--){ //输入第二个多项式scanf("%d%d",&a1,&a2);AddList(&H2,a1,a2);}//LTraverse(H1);//LTraverse(H2);Add_LinkList(&H1,&H2,&H3);//做加法运算LTraverse(H3);//打印最终结果}
【数据结构】--章节2.3----线性表的链式表示和实现相关推荐
- 从零开始学数据结构和算法(二)线性表的链式存储结构
链表 链式存储结构 定义 线性表的链式存储结构的特点是用一组任意的存储单元的存储线性表的数据元素,这组存储单元是可以连续的,也可以是不连续的. 种类 结构图 单链表 应用:MessageQueue 插 ...
- 数据结构开发(5):线性表的链式存储结构
0.目录 1.线性表的链式存储结构 2.单链表的具体实现 3.顺序表和单链表的对比分析 4.小结 1.线性表的链式存储结构 顺序存储结构线性表的最大问题是: 插入和删除需要移动大量的元素!如何解决? ...
- 数据结构第三篇——线性表的链式存储之单链表
♥注:未经博主同意,不得转载. 线性表的链式存储结构的特点是用一组任意的存储单元来存储线性表的数据元素,这些单元可以分散在内存中的任意位置上,其在物理上可以是连续的,也可以是不连续的.具有链式存储结构 ...
- 数据结构(四)—— 线性表的链式存储
线性表的链式存储 目录 1.链式存储结构与顺序存储结构的比较 2.单链表.双链表与循环链表 3.单链表的实现 1.链式存储结构与顺序存储结构的比较 顺序存储的特点:已物理相邻表示逻辑关系,任一元素均可 ...
- 严蔚敏数据结构C语言版——线性表的链式存储方式详细代码
一.严蔚敏数据结构C语言版 由于书上的许多地方都是伪代码,所以下面的代码对课本上的做了一些改动,使代码能够正常运行 链表的定义即相关类型定义 typedef int ElementType; type ...
- 数据结构和算法:(3)3.2线性表的链式存储结构
线性表的链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素也就是说你这个可以放在A地点,这个可以放在E地点,A地点和E地点中间可以隔开一个C地点和D地点,这样是允许的),这组存储单元可以存在 ...
- 数据结构——线性表的链式表示
线性表的定义 线性表是具有相同数据类型的n(n >= 0)个数据元素的有限序列.其中n为表长,当n = 0时,该线性表是一个空表.若用L命名线性表,其一般表示为:L = (a1, a2, ... ...
- 《数据结构》c语言版学习笔记——其他链表(线性表的链式存储结构Part2)
线性表的链式存储结构 数据结构系列文章 第三章 循环链表.双向链表 文章目录 线性表的链式存储结构 前言 一.循环链表 (一)定义 (二)尾指针 二.双向链表 (一)定义 (二)代码 总结 前言 提示 ...
- 《数据结构》c语言版学习笔记——单链表结构(线性表的链式存储结构Part1)
线性表的链式存储结构 数据结构系列文章 第二章 单链表结构 文章目录 线性表的链式存储结构 前言 一.单链表的建立 代码 二.单链表的读取 代码 三.单链表的插入 代码 四.单链表的删除 代码 五.单 ...
- 数据结构与算法笔记(三) 线性表(链式描述) 链表
在链式描述中,线性表元素的位置在内存中是随机的,每个元素都有一个明确的指针指向线性表的下一个元素的位置. 1.单向链表: 数据对象的每一个元素都用一个单元或者节点来描述,每个节点都明确包含另一个相关节 ...
最新文章
- java 深克隆(深拷贝)与浅克隆(拷贝)详解
- 【数据结构与算法】之深入解析十大常用排序算法的原理分析和算法实现
- SpringSecurity常用过滤器介绍
- 计算机专业导论论文2000字,计算机专业导论论文.doc
- postman里测试文件上传(MultipartFile)
- nginx子目录301重定向到其他域名URL
- MybatisGenerator+lombok
- sublime text 3 3143
- 迷你世界显示服务器错误,迷你世界登陆失败怎么办-迷你世界登陆未成功_牛游戏页游...
- Dataframe修改列名
- python二元函数图像在线绘制_numpy,matplotlib
- YDOOK: Python:requests: Scanic: 客户端 client 向服务器请求照片并返回 post请求
- 2021-06-10 MYSQL存储过程与触发器作业
- 文件上传页面出现400错误的问题
- [Openstack]一键安装Openstack测试环境
- Python代码列主元消去法matlab编程_当面试官要求现场手敲代码,如何体现你对Python的编程能力?...
- ES8(Java API Client)查询详解
- mac下载、破解、安装webstorm编辑器
- Android 外挂--- 微信自动聊天
- 【融职培训】Web前端学习 第3章 JavaScript基础教程9 内置对象