当前位置: 首页 > news >正文

【408 数据结构】第2章 线性表

文章目录

  • 线性表
    • 考纲
    • 线性表的定义和基本操作
      • 1. 定义
      • 2. 线性表的基本操作
    • 线性表的顺序表示
      • 1. 顺序表的定义
      • 2. 顺序表基本操作的实现
        • 初始化
        • 插入-时间复杂度O(n)
        • 删除-时间复杂度O(n)
        • 按值查找-时间复杂度O(n)
    • 线性表的链式表示
      • 1. 单链表的定义
      • 2. 单链表基本操作的实现
        • 单链表的初始化
        • 求表长 O(n)
        • 按序号查找结点 O(n)
        • 按值查找结点 O(n)
        • 插入结点 O(n)
        • 删除结点 O(n)
        • 头插法建立单链表 O(n)
        • 尾插法建立单链表 O(n)
      • 3. 双链表
        • 双链表的插入
        • 双链表的删除
      • 4. 循环链表
        • 循环单链表
        • 循环双链表
      • 5. 静态链表
    • 小结

在这里插入图片描述

线性表

考纲

  • 线性表基本概念和实现
  • 顺序存储和链式存储
  • 线性表的应用

在这里插入图片描述

线性表的定义和基本操作

1. 定义

线性表:具有相同数据类型的n个数据元素的有限序列。

n为表长,值为0表示空表。第一个元素和最后一个元素叫表头元素表尾元素

元素有且只有一个前驱和后继,表头元素无前驱,表尾元素无后继。

线性表特点

  • 元素个数有限
  • 元素逻辑上有顺序性
  • 元素都是数据元素
  • 元素的数据类型相同,占用存储空间大小相同
  • 元素具有抽象性,不考虑元素所表示的内容,只讨论元素间的逻辑关系。

线性表、顺序表和链表的比较:

​ 线性表表示的时一种逻辑结构,顺序表和链表表示的是存储结构

2. 线性表的基本操作

InitList(&L)       //初始化表,建立一个空表
Length(L)          //表长
LocateElem(L,e)    //按值查找,e是值
GetElem(L,i)       //按位查找,i是位置
ListInsert(&L,i,e)  //将e插入到L表的i位置
ListDelete(&L,i,e)  //删除L表中i位置的元素e  
PrintList(L)        //按顺序输出表L的元素
Empty(L)            //判空,若L为空则返回True    
DestroyList(&L)     //销毁L表,释放内存

在这里插入图片描述

线性表的顺序表示

1. 顺序表的定义

顺序表:按顺序存储的线性表。任意一个数据元素都可以实现随机存取

特点:表中元素的逻辑顺序和存储的物理顺序相同。

位序:元素在顺序表的位置。

线性表中元素的位序是从1开始的,数组中元素的下标是从0开始的。

一维数组静态分配,大小和空间事先固定,空间占满会溢出,程序崩溃。

//静态分配的顺序表 存储结构 描述
#define MaxSize 50             //顺序表最大长度
typedef struct{ElemType data[MaxSize];    //元素,ElemType是数据类型int Length;                //顺序表的当前长度
}SqLst;                        //顺序表的类型定义

一维数组动态分配时,一旦数据空间占满,另开辟更大的存储空间,将原来表中的元素拷贝到新空间。

//静态分配的顺序表 存储结构 描述#define InitSize 100                //表长度的初始定义
typedef struct{ElemType *data;                //指示动态分配数组的指针int MaxSize,Length;            //数组的最大容量和当前个数
}SeqList;                          //动态分配 顺序表的类型定义L.data = (ElemType*)malloc(sizeof(ElemType)*InitSize);     //c初始动态分配语句
L.data = new ElemType[InitSize];                           //c++初始动态分配语句

动态分配不是链式存储,是顺序存储!!!

注意看这两段代码的不同之处!

malloc(memory allocation的缩写)方法用于动态分配具有指定大小的单个大块内存。它返回void类型的指针,其指针类型可以灵活转换,同时使用默认garbage value(内存中存储的随机值)初始化内存块。其语法格式为:
    ptr = (指针转换格式*) malloc(分配的字节数)

顺序表的优点:随机访问、存储密度高。

顺序表的缺点:元素的插入和删除需要移动大量元素;需要连续的存储空间。

2. 顺序表基本操作的实现

只描述 初始化、插入、删除、按值查找 这四个操作。

初始化

动态分配和静态分配的初始化操作有所不同。

之前学习到,初始化操作的代码是:InitList(&L);

静态分配的顺序表,分配的空间固定,初始化只需要长度设置为0

//SqList L
void InitList(SqList &L){L.Length = 0;
}

动态分配的顺序表要分配存储空间,定义长度为0,设置最大容量。

void InitList(SeqList &L){L.data = (ElemType *)malloc(InitSize*sizeof(ElemType));L.Length = 0;L.MaxSize = InitSize;
}
插入-时间复杂度O(n)

在顺序表L的第i个位置上插入元素e

若i不合法,返回false;i合法则第i个元素及其后面的元素全部后移1位,插入元素,L长度加一,返回true。

bool ListInsert(SqList &L,int i,ElemType e){if(i<1 | i>L.Length+1)return false;if(L.length >= MaxSize)return false;for(j=L.Length;j>i;j--)L.data[j] = L.data[j-1];   //从最后一个元素开始后移,不理解就在纸上写一个数组,后移观察下标变化L.data[i-1] = e;L.length++;return true;
}
删除-时间复杂度O(n)

在顺序表L的第i个位置上删除元素e,和插入异曲同工。插入操作元素是后移,删除操作元素是前移。

bool ListInsert(SqList &L,int i,ElemType e){if(i<1 | i>L.Length+1)return false;L.data[i-1] = e;        //将删除的元素赋值for(j=i;j<L.length;j++)L.data[j-1] = L.data[j];   //从第i个元素开始前移L.Length--;return true;}
按值查找-时间复杂度O(n)

在顺序表中查找元素值等于e的元素,返回位序i。

int LocateElem(SqList &L,ElemType e){int i;   //定义位序for(i=0,i<L.Length;i++)if(L.data[i] = e)return i+1;return 0;   //退出循环,说明查找失败
}

加油加油,学习过半~~
在这里插入图片描述

线性表的链式表示

1. 单链表的定义

单链表:线性表的链式存储。不可随机存取

单链表的结构:data为数据域,存放数据元素;next为指针域,存放后继结点的地址。

在这里插入图片描述

单链表结点类型的描述如下:

typedef struct DNode{ElemType data;   //数据域struct LNode *next;  //指针域
}LNode, *LinkList;

使用头指针L来标识一个单链表,指出链表的起始地址,头指针为NULL标识一个空表。

头结点一般不携带信息。

通常单链表附加一个头结点,头指针L指向头结点。不带头结点的话,指针L指向第一个数据结点。

【考研】分清带头结点和不带头结点的单链表-CSDN博客

引入头结点的两个优点:

1.无需对第一个数据结点进行特殊处理

2.无论链表是否为空,头指针都指向头结点的非空指针(空表中的头结点的指针域为空),因此空表和非空表的处理得到了统一。

2. 单链表基本操作的实现

无特殊说明,本节默认携带头结点

单链表的初始化

带头结点:创建头结点,头指针指向头结点,头结点next域初始化为NULL。

bool InitList(LinkList &L){L = (LNode*)malloc(sizeof(LNode));   //创建头结点L->next = NULL;return true;
}

不带头结点:头指针初始化为NULL

bool InitList(LinkList &L){L = NULL;   //头指针初始化为NULLreturn true;
}
求表长 O(n)

计算单链表中数据结点的个数。

设置一个计数变量len,访问一个结点便+1,直到访问到空结点。

int length(LinkList L){int len = 0;   //计数变量LNode *p = L;while(P->next!=NULL){p = p->next;len++;}return len;
}

单链表的长度不包含头结点!!

按序号查找结点 O(n)

从第一个结点开始,沿着next域从前往后寻找,直到找到第i个结点,返回该结点的指针。

LNode *GetElem(LinkList L,int i){LNode *p = L;  //p指向第一个结点int j = 0;while(p!NULL && j<i){p=p->next;   //指针向后移动j++;}return p;
}
按值查找结点 O(n)

从第一个结点开始,依次比较各结点的数据域,当某结点的data域等于要查找的e值,则返回该指针

LNode *LocateElem(LinkList L,ElemType e){LNode *p = L->next;while(p->data!=e && p!=NULL){   //不满足条件,指针就后移p=p->next;}return p;
}
插入结点 O(n)

将值为x的结点插入到单链表的第i个位置。

①和②的顺序一定不能反,不然会丢失后面的数据,操作就失败了

在这里插入图片描述

bool ListInsert(LinkList &L,int i,ElemType e){LNode *p = L;  //p指向第一个结点int j = 0;   //记录当前结点的位序while(p!=NULL && j<i-1){   //j小于i-1时指针就后移,直到找到第i-1个结点p=p->next;j++;}if(p==NULL)    return false;LNode *s = (LNode*)malloc(sizeof(LNode));   //创建ss->data = e;          //data数据域值为es->next = p->next;    //①p->next = s;          //②return true;
}

上述插入为 后插

前插:在某结点前插入一个元素

前插可以转化为后插操作来实现:就是正常进行后插操作后,前后两个结点的data互换一下,这样在逻辑上也就实现了效果。

s->next = p->next;
p->next = s;
temp = p->data;    //借助temp作为容器,暂放数据
p->data = s->data;
s->data = temp;
删除结点 O(n)

删除单链表的第i个节点

在这里插入图片描述

bool ListDelete(LinkList &L,int i,ElemType &e){LNode *p = L;   //p指向头结点int j = 0;   //计数while(p!=NULL&&j<i-1){p=p->next;j++;}while(p==NULL)return false;LNode *q = p->next;    //q指向p的下一个结点q->data = e;   //记录一下被删除的结点p->next = q->next;free(q);     //释放内存return true;
}
头插法建立单链表 O(n)

在这里插入图片描述

LinkList List_HeadInsert(LinkList &L){LNode *p;int x;    //设置元素类型L = (LNode *)malloc(sizeof(LNode));   //创建头结点L->next = NULL;    //初始化为空表scanf("%d",&x);     //输入结点的值while(x!==9999){p = (LNode*)malloc(sizeof(LNode));  //创建新结点p->data = x;p->next = L->next;  //①L->next = p;    //②scanf("%d",&x);}return L;
}

读入数据的顺序和生成的链表的元素的顺序是相反的!,可以用来实现链表的逆置

尾插法建立单链表 O(n)

增加一个尾指针r,始终指向单链表的最后一个元素。

在这里插入图片描述

LinkList List_TailInsert(LinkList &L){int x;L = (LNode *)malloc(sizeof(LNode));   //创建头结点LNode *s,*r = L;    //r为尾指针,指向Lscanf("%d",&x);     //输入结点的值while(x!==9999){s = (LNode*)malloc(sizeof(LNode));  //创建新结点s->data = x;   //赋值r->next = s;    r=s;        //指针后移scanf("%d",&x);}r->next = NULL;   //尾指针的next域置空return L;
}

3. 双链表

单链表结点中只要一个指向其后继的指针,使得单链表只能从前往后依次遍历。

双链表中有两个指针,分别为priornext,分别指向直接前驱直接后继

在这里插入图片描述

双链表的数据结构描述如下:

typedef struct DNode{ElemType data;struct DNode *prior,*next;
}DNode,*DLinkList;

双链表的按值查找和按位查找和单链表相同,为O(n)

插入删除操作的时间复杂度为O(1)

双链表的插入

在这里插入图片描述

主要操作代码如下:

① s->next = p->next;
p->next->prior = s;
s->prior = p;
④ p->next = s;

插入的语句不唯一,但是①必须要在④的前面!!!

双链表的删除

在这里插入图片描述

p->next = q->next;
q->next->prior = p;
free(q);

在这里插入图片描述

4. 循环链表

循环单链表

其和单链表的区别:最后一个结点的next不是NULL,而是指向头结点,从而形成一个环。
在这里插入图片描述

循环单链表的插入和删除算法和单链表几乎一样,但是操作若是在表尾进行,循环单链表不需要判断是否处于表尾。

循环单链表可以从表中的任意位置开始遍历整个单链表。

循环双链表

在这里插入图片描述

在循环双链表中,头结点的prior指针还要指向表尾结点。

5. 静态链表

静态链表是用数组来描述线性表的链式存储结构,结点也有datanext

但是这里的指针指的是 结点在数组中的相对地址,称为游标。如下图,a的next存储的是1,对应的是b,b的next存储的是6,对应的是c。

静态链表需要一块连续的存储空间。

在这里插入图片描述

图有点黑因为宿舍关灯了hhh

静态链表的结构类型的描述如下:

#define MaxSize 50;
typedef struct {ElemType data;int next;
}SLinkList[MaxSize];

小结

基础知识部分完成

后续将 课后题 拉出来详解

多敲代码,掌握基础结构的书写!!!
持续更新~~
在这里插入图片描述

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • PHP-SER-libs靶场通关(1-9)
  • 数据结构基础详解(C语言): 栈的括号匹配(实战)与栈的表达式求值特殊矩阵的压缩存储
  • 国家新标准引领,油烟净化器为烟火气添清新活力
  • 网络安全(sql注入2,less3)
  • 苹果的“AI茅”之路只走了一半
  • TeamTalk数据库代理服务器
  • AI问答-数据库:理解头表和行表
  • ModuleNotFoundError: No module named ‘keras.layers.core‘怎么解决
  • 【CSS】mask-image属性的详细介绍
  • Java中校验导入字段长度与数据库字段长度一致性
  • 图为科技基于昇腾AI,打造智慧工厂检测解决方案
  • 金蝶云星空查询SQL
  • 数据仓库理论知识
  • 2024/9/9 408“回头看”:b树
  • Spark-ShuffleWriter
  • CAP 一致性协议及应用解析
  • JavaScript标准库系列——Math对象和Date对象(二)
  • MySQL几个简单SQL的优化
  • OSS Web直传 (文件图片)
  • SpiderData 2019年2月13日 DApp数据排行榜
  • 初探 Vue 生命周期和钩子函数
  • 动态规划入门(以爬楼梯为例)
  • 机器学习学习笔记一
  • 使用 @font-face
  • 我的面试准备过程--容器(更新中)
  • 字符串匹配基础上
  • Hibernate主键生成策略及选择
  • 支付宝花15年解决的这个问题,顶得上做出十个支付宝 ...
  • ​学习一下,什么是预包装食品?​
  • #VERDI# 关于如何查看FSM状态机的方法
  • (02)Hive SQL编译成MapReduce任务的过程
  • (2)(2.4) TerraRanger Tower/Tower EVO(360度)
  • (C语言)深入理解指针2之野指针与传值与传址与assert断言
  • (NO.00004)iOS实现打砖块游戏(九):游戏中小球与反弹棒的碰撞
  • (pycharm)安装python库函数Matplotlib步骤
  • (SpringBoot)第二章:Spring创建和使用
  • (八)Flink Join 连接
  • (超详细)2-YOLOV5改进-添加SimAM注意力机制
  • (九)c52学习之旅-定时器
  • (每日一问)计算机网络:浏览器输入一个地址到跳出网页这个过程中发生了哪些事情?(废话少说版)
  • (七)glDrawArry绘制
  • (十六)视图变换 正交投影 透视投影
  • (转)linux 命令大全
  • (转)项目管理杂谈-我所期望的新人
  • .a文件和.so文件
  • .bat批处理(十一):替换字符串中包含百分号%的子串
  • .net core 源码_ASP.NET Core之Identity源码学习
  • .NET/C# 编译期间能确定的相同字符串,在运行期间是相同的实例
  • .NET成年了,然后呢?
  • .Net程序猿乐Android发展---(10)框架布局FrameLayout
  • .NET简谈设计模式之(单件模式)
  • @SpringBootApplication 包含的三个注解及其含义
  • @test注解_Spring 自定义注解你了解过吗?
  • @transaction 提交事务_【读源码】剖析TCCTransaction事务提交实现细节
  • [《百万宝贝》观后]To be or not to be?