临阵磨枪,不快也光。应付考试,大量摘抄课本内容,会陆续添加其他相关内容及代码。最后祝我考试顺利。

逻辑结构:


存储结构:简单来说:逻辑结构在存储器中的映像(表示) 。书本上定义:数据对象在计算机中的存储表示称为数据的存储结构,也称为物理结构。把数据对象存储到计算机时,通常要求既要存储各数据元素的数据,又要存储数据元素之间的逻辑关系,数据元素在计算机内用一个结点来表示。

由n(n>=O)个数据特性相同的元素构成的有限序列称为线性表。
线性表中元素的个数n(n>=O)定义为线性表的长度,n=O时称为空表。
对于非空的线性表或线性结构,其特点是:
线性表的顺序表示指的是用一组地址连续的存储单元依次存储线性表的数据元素, 这种表示也称作线性表的顺序存储结构或顺序映像。通常,称这种存储结构的线性表为顺序表(SequentialList)。其特点是,逻辑上相邻的数据元素,其物理次序也是相邻的。
- #include
- #include
-
- #define Size 10//宏定义,表示线性表的长度
-
- typedef struct{
- int *head;//定义一个长度不确定的数组,‘动态数组’
- int length;//定义线性表的长度
- int size;//定义线性表的存储容量
- }sequenceTable;
-
- void initTable(sequenceTable *t){//初始化顺序表
- t->head=(int *)malloc(Size*sizeof(int));//动态申请线性表存储空间
- if(!t->head){//申请空间失败,初始化失败,提示后退出程序
- printf("初始化失败\n");
- exit(0);
- }
- //申请内存空间成功,将表长度初始为0,存储容量初始为Size
- t->length=0;
- t->size=Size;
- }
- //插入fn-ele插入元素,i-插入位置
- void insertTable(sequenceTable *t,int ele, int i){
- if(i>t->length+1||i<1){
- printf("插入地址超出线性表地址范围\n");
- return;
- }
- if(t->length==t->size){//如果内存空间不足,需要额外申请空间
- t->head=(int *)realloc(t->head,(t->length+1)*sizeof(int));
- if(!t->head){
- printf("线性表内存申请失败");
- return;
- }
- t->size++;
- }
- /*插入操作,所有元素先后移一位在插入
- 用户输入的位置比实际位置大1,因此需要j>=i-1*/
- for(int j=t->length;j>=i-1;j--){
- t->head[j+1]=t->head[j];
- }
- t->head[i-1]=ele;
- t->length++;
- }
- //删除fn
- void delTable(sequenceTable *t,int i){
- if(i>t->length||i<1){
- printf("删除失败!");
- return;
- }
- //删除
- for(int j=i;j
length;j++){ - t->head[j-1]=t->head[j];
- }
- t->length--;
- }
- //查询fn
- int searchTable(sequenceTable t,int ele){
- for(int i=0;i
- if(t.head[i]==ele){
- return i+1;
- }
- }
- return -1;
- }
- //修改fn
- void updateTable(sequenceTable *t,int ele,int replace){
- int searchResult=searchTable(*t,ele);
- if(searchResult>0){
- t->head[searchResult-1]=replace;
- }else{
- printf("被替换的数据不存在\n");
- }
- }
- void showData(sequenceTable t1){
- printf("输出线性表元素:");
- for(int i=0;i
- printf("%d ",t1.head[i]);
- }
- printf("\n");
- }
- int main()
- {
- sequenceTable t1={NULL,0,0};//初始化动态指针,t1={NULL,0,0}
- initTable(&t1);
- for(int i=0;i
//给线性表添加元素 - t1.head[i]=i+1;
- t1.length++;
- }
- showData(t1);
- //插入元素
- insertTable(&t1,11,2);
- showData(t1);
- //删除元素
- delTable(&t1,2);
- showData(t1);
- //修改元素
- updateTable(&t1,3,33);
- showData(t1);
- //释放申请内存
- free(t1.head);
- return(0);
- }
线性表的链接存储结构及实现
线性表链式存储结构的特点是:用一组任意的存储单元存储线性表的数据元素(这组存储单元可以是连续的,也可以是不连续的)。因此,为了表示每个数据元素
,与其直接后继数据元素
之间的逻辑关系,对数据元素
来说,除了存储其本身的信息之外,还需存储一个指示其直接后继的信息(即直接后继的存储位置)。这两部分信息组成数据元素
的存储映像,称为结点(node)。它包括两个域:其中存储数据元素信息的域称为数据域;存储直接后继存储位置的域称为指针域。指针域中存储的信息称作指针或链。n个结点(
,(1<=i<=n)的存储映像)链结成一个链表,即为线性表(
,
,··,
)的链式存储结构。又由于此链表的每个结点中只包含一个指针域,故又称线性链表或单链表。
根据链表结点所含指针个数、指针指向和指针连接方式,可将链表分为单链表、循环链表、双向链表、二叉链表、十字链表、邻接表、邻接多重表等。其中单链表、循环链表和双向链表用千实现线性表的链式存储结构,其他形式多用于实现树和图等非线性结构。
单链表
用单链表表示线性表时,数据元素之间的逻辑关系是由结点中的指针指示的。换句话说,指针为数据元素之间的逻辑关系的映像,则逻辑上相邻的两个数据元素其存储的物理位置不要求紧邻,由此,这种存储结构为非顺序映像或链式映像。通常将链表画成用箭头相链接的结点的序列,结点之间的箭头表示链域中的指针。在使用链表时,我们关心的只是它所表示的线性表中数据元素之间的逻辑顺序,而不是每个数据元素在存储器中的实际位置。

- #include
- #include
-
- typedef struct link{
- int ele;//数据域
- struct link *next;//指针域,指向后继元素。因常用next,又叫Next域
- }Link;
-
- Link* initLink(int num){
- //创建头指针
- Link* p=NULL;
- //创建临时变量temp暂时当作首元结点
- Link* temp=(Link*)malloc(sizeof(Link));
- temp->ele=0;//有头给首元结点赋值
- //temp->ele=1;//无头给首元结点赋值
- temp->next=NULL;//首元结点后继节点暂时置为NULL
- p=temp;//头指针指向首元结点
- //创建节点
- for(int i=1;i
//有头 - //for(int i=2;i
- Link* a=(Link*)malloc(sizeof(Link));
- a->ele=i;//给创建节点数据域赋值
- a->next=NULL;//给创建节点指针域赋值
- temp->next=a;//每次temp的后继节点指向当前的a
- temp=temp->next;//给每次temp赋值为当前的a;此处temp=a;也可
- }
- return p;//返回链表
- }
- Link* insertLink(Link* p,int ele,int index){
- // if(index==1){//无头解开注释
- // Link* new=(Link*)malloc(sizeof(Link));
- // new->ele=ele;
- // new->next=p;
- // p=new;
- // return p;
- // }
- Link* new=NULL;
- Link* temp=p;
- for(int i=1;i
- //for(int i=1;i
- temp=temp->next;
- if(temp==NULL){
- printf("插入元素失败!!!\n");
- return p;
- }
- }
- //创建节点
- new=(Link*)malloc(sizeof(Link));
- new->ele=ele;
- new->next=temp->next;//将temp的后继节点地址赋值给插入元素的后继节点
- temp->next=new;//temp的后继节点指向new
- return p;
- }
- void updateLink(Link* p,int oldEle,int newEle){
- p=p->next;//有头加这句,无头注释这句
- while(p){
- if(p->ele==oldEle){
- p->ele=newEle;
- return;
- }
- p=p->next;
- }
- printf("更新元素失败\n");
- }
- int delLink(Link* p,int ele){
- //int delLink(Link* *p,int ele){//无头解开注释
- Link* del=NULL;
- Link* temp=p;//有头
- // Link* temp=*p;//无头解开注释
- // if(temp->ele==ele){//无头解开注释
- // (*p)=(*p)->next;
- // free(temp);
- // return 1;
- // }
- int find=-1;
- while(temp->next){
- if(temp->next->ele==ele){
- find=1;
- break;
- }
- temp=temp->next;
- }
- if(find==-1){
- printf("删除失败,该元素不在链表中\n");
- return 0;
- }
- del=temp->next;
- temp->next=temp->next->next;
- free(del);
- return 1;
- }
- int selectLink(Link* p,int ele){
- int i=1;
- p=p->next;//有头节点加这句,无头结点注释这句
- while(p){
- if(p->ele==ele){
- printf("查找成功,%d元素在链表中第%d位\n",ele,i);
- return i;
- }
- p=p->next;
- i++;
- }
- printf("查找失败,%d元素不在链表中\n",ele);
- return -1;
- }
- void displayLink(Link* p){//循环输出链表数据
- p=p->next;//有头节点加这句,无头结点注释这句
- while(p){
- printf("%d ",p->ele);
- p=p->next;
- }
- printf("\n");
- }
- void LinkFree(Link* p){//释放链表
- //解决free(): double free detected in tcache 2
- Link* fr=NULL;
- while(p->next){
- fr=p->next;
- p->next=p->next->next;
- free(fr);
- }
- free(p);
- }
- int main()
- {
- int num=5;
- Link* p=NULL;
- printf("初始化链表:");
- p=initLink(num);
- displayLink(p);
- printf("在链表第1位插入9:");
- p=insertLink(p,9,1);
- displayLink(p);
- printf("删除链表元素9:");
- //delLink(&p,9);//无头结点删除
- delLink(p,9);//有头结点删除
- displayLink(p);
- selectLink(p,4);
- printf("更新链表元素4为6:");
- updateLink(p,4,6);
- displayLink(p);
- LinkFree(p);
- return(0);
- }
- /* 标准输出:
- 初始化链表:1 2 3 4
- 在链表第1位插入9:9 1 2 3 4
- 删除链表元素9:1 2 3 4
- 查找成功,4元素在链表中第4位
- 更新链表元素4为6:1 2 3 6
- */
循环链表
循环链表(CircularLinked List)是另一种形式的链式存储结构。其特点是表中最后一个结点的指针域指向头结点,整个链表形成一个环。由此,从表中任一结点出发均可找到表中其他结点,图2.17所示为单链的循环链表。类似地,还可以有多重链的循环链表。

循环单链表的操作和单链表基本一致,差别仅在于:当链表遍历时,判别当前指针p是否指向表尾结点的终止条件不同。在单链表中,判别条件为p!=NULL或p->next!=NULL,而循环单链表的判别条件为p!=L或p->next!=L。在某些情况下,若在循环链表中设立尾指针而不设头指针(见图2.18(a)),可使一些操作简化。例如,将两个线性表合并成一个表时,仅需将第一个表的尾指针指向第二个表的第一个结点,第二个表的尾指针指向第一个表的头结点,然后释放第二个表的头结点。当线性表以图2.18(a)的循环链表作存储结构时,这个操作仅需改变两个指针值即可,主要语句段如下:
- p=B->next->next;
- B->next=A->next;
- A->next=p;
上述操作的时间复杂度为0(1),合并后的表如图2.18(b)所示。

双向链表
单链表和循环链表的链式存储结构的结点中只有一个指示直接后继的指针域,由此,从某个结点出发只能顺指针向后寻查其他结点。若要寻查结点的直接前驱,则必须从表头指针出发。换句话说,在单链表中,查找直接后继结点的执行时间为0(1),而查找直接前驱的执行时间为O(n)。为克服单链表这种单向性的缺点,可利用双向链表(DoubleLinked List)。顾名思义,在双向链表的结点中有两个指针域,一个指向直接后继,另一个指向直接前驱。结点结构如图2.19(a)所示,在C语言中可描述如下:
- //双向链表的存储结构
- typedef struct DuLNode{
- ElemType data; //数据域
- struct DuLNode *prior;//直接前驱
- struct DuLNode *next;//直接后继
- }DuLNode,*DuLinkList;
和单链的循环表类似,双向链表也可以有循环表,如图2.19(c)所示,链表中存有两个环,图2.19(b)所示为只有一个表头结点的空表。

在双向链表中,若d为指向表中某一结点的指针(即d为DuLinkList型变量),则显然有
d->next->prior = d->prior->next = d
这个表示方式恰当地反映了这种结构的特性。
顺序表和链表的比较
-
空间性能的比较
-
存储空间的分配顺序表的存储空间必须预先分配,元素个数扩充受一定限制,易造成存储空间浪费或空间溢出现象;而链表不需要为其预先分配空间,只要内存空间允许,链表中的元素个数就没有限制。基于此,当线性表的长度变化较大,难以预估存储规模时,宜采用链表作为存储结构。
-
存储密度的大小链表的每个结点除了设置数据域用来存储数据元素外,还要额外设置指针域,用来存储指示元素之间逻辑关系的指针,从存储密度上来讲,这是不经济的。所谓存储密度是指数据元素本身所占用的存储量和整个结点结构所占用的存储量之比,即
存储密度越大,存储空间的利用率就越高。显然,顺序表的存储密度为1而链表的存储密度小于1。如果每个元素数据域占据的空间较小,则指针的结构性开销就占用了整个结点的大部分空间,这样存储密度较小。例如,若单链表的结点数据均为整数,指针所占用的空间和整型量相同,则单链表的存储密度为0.5。因此,如果不考虑顺序表中的空闲区,则顺序表的存储空间利用率为100%,而单链表的存储空间利用率仅为50%。基于此,当线性表的长度变化不大,易于事先确定其大小时,为了节约存储空间,宜采用顺序表作为存储结构。
- 时间性能的比较
- 存取元素的效率顺序表是由数组实现的,它是一种随机存取结构,指定任意一个位置序号i都可以在0(1)时间内直接存取该位置上的元素,即取值操作的效率高;而链表是一种顺序存取结构,按位置访问链表中第i个元素时,只能从表头开始依次向后遍历链表,直到找到第i个位置上的元素,时间复杂度为O(n),即取值操作的效率低。基于此,若线性表的主要操作是和元素位置紧密相关的这类取值操作,很少做插入或删除时,宜采用顺序表作为存储结构。
- 插入和删除操作的效率对千链表,在确定插入或删除的位置后,插入或删除操作无需移动数据,只需要修改指针,时间复杂度为0(1)。而对千顺序表,进行插入或删除时,平均要移动表中近一半的结点,时间复杂度为O(n)。尤其是当每个结点的信息量较大时,移动结点的时间开销就相当可观。基于此,对于频繁进行插入或删除操作的线性表,宜采用链表作为存储结构。
参考书目:严蔚敏-《数据结构(C语言版)》
-
相关阅读:
PaddlePaddle:开源深度学习平台
小程序毕设作品之微信汽车维修保养小程序毕业设计成品(3)后台功能
Golang gorm 一对一关系
前端开发:4、JavaScript简介、变量与常量、数据类型及内置方法、运算符、流程控制、循环结构、内置方法
文档管理系统:攻克这3个痛点,解决80%企业文档管理难题
nrf52832 开发板入手笔记:J-Flash 蓝牙协议栈烧写
培训心得怎么写?CHAT帮你解决问题
HTML+CSS+JavaScript仿京东购物商城网站 web前端制作服装购物商城 html电商购物网站
中国传统美食网页HTML代码 学生网页课程设计期末作业下载 美食大学生网页设计制作成品下载 DW餐饮美食网页作业代码下载
Python中的三元运算符详解
-
原文地址:https://blog.csdn.net/u012761510/article/details/127573488