数据结构 第二章 线性表

目录

第二章 线性表

一、线性表的定义和基本操作

1.线性表的定义

1.1什么是线性表

1.2线性表的几个概念

2.线性表的基本操作

                                       二、顺序表(顺序存储)

1.顺序表的实现—动态分配

2.顺序表的特点:

3.顺序表的插入和删除

插入操作代码

​编辑插入操作时间复杂度分析

删除操作代码

删除操作时间复杂度分析

4.顺序表的查找

按位序查找代码

按位序查找时间复杂度分析

按值查找代码

按值查找时间复杂度分析

                                         三、链表(链式存储)

1.单链表

定义:

初始化(带头结点):

插入:

按位序插入(带头结点)

按位序插入(不带头结点)

前插操作

删除:

按位序删除(带头结点)

指定节点的删除

单链表的查找(带头结点)

按位查找

按值查找

时间复杂度

单链表的建立

尾插法

头插法

2.双链表

初始化(带头结点)

插入:

删除:

遍历

3.循环链表

循环单链表 初始化:

循环单链表 头插,尾插

循环双链表 初始化:

循环双链表 插入:

循环双链表 删除:

4.静态链表

什么是静态链表?

定义一个静态链表

简述基本操作实现

初始化

查找,插入,删除


第二章 线性表

一、线性表的定义和基本操作

1.线性表的定义

1.1什么是线性表

线性表是具有相同数据类型的n(n>0)个数据元素有限序列,其中n为表长,当n=0时线性表是一个空表。若用L命名线性表,则其一般表示为

L = (a1,a2, ... ,ai,ai+1, ... ,an)

1.2线性表的几个概念

ai线性表中的“第i个”元素线性表中的位序。(注意:位序是从1开始,数组下标是从0开始)

a1表头元素:an是标尾元素

除第一个元素外,每个元素有且仅有一个直接前驱;除最后一个元素外,每个元素有且仅有一个直接后继

2.线性表的基本操作

创销、增删查改(所有数据结构适用的记忆思路)

判空、判长,打印输出(还可以根据自己的需求增加其他的基本操作)

其他值得注意的点:要 改变 传入的参数要把对应的地址传过去、函数名要有可读性

                                       二、顺序表(顺序存储)

定义:顺序表——用顺序存储的方式实现线性表

顺序存储:把逻辑上相邻的元素存储在物理位置上也相邻的存储单元中,元素之间的关系由存储单元的邻接关系来体现

判断一个数据元素的大小:

C语言 sizeof(ElemType)

1.顺序表的实现—动态分配

C ——malloc、free函数

注意:malloc函数返回一个指针,需要强制转换为你定义的数据元素类型指针。

(ElemType*)malloc(sizeof(ElemTyped)*InitSize);

当顺序表存满时,可再用malloc动态拓展 顺序表的最大容量(realloc也可)

需要将数据元素赋值到新的存储区域,并用free函数释放原区域

2.顺序表的特点:

  • 随机访问,即可以在O(1)时间内找到第i个元素。
  • 存储密度高,每个结点只存储数据元素

链式存储多了存放指针的内存

  • 拓展容量不方便,静态不能拓展(即便采用动态分配的方式实现,拓展长度时间复杂度也很高,因为要把数据拷贝到新的区域)
  • 插入、删除操作不方便,需要移动大量元素

3.顺序表的插入和删除

插入操作代码

插入操作时间复杂度分析

n = L.length(表长)

删除操作代码

删除操作时间复杂度分析

4.顺序表的查找

按位序查找代码

GetElem(L,i); 按位查找操作。获取表L中第i位置元素的值

静态:

动态:

再次理解:动态内存访问时,malloc函数返回的存储空间地址,要转换成与数据元素的数据结构相对应指针

按位序查找时间复杂度分析

时间复杂度:O(1)

由于顺序表的各个数据元素在内存中连续存放,因此可以根据起始地址和数据元素大小立刻找到第i个元素—“随机存取”特性

按值查找代码

LocateElem(L,e); 按值查找操作。在表L中查找具有给定关键字的元素

注意:结构体类型的变量不能直接用“ == ”进行比较的

按值查找时间复杂度分析

                                         三、链表(链式存储)

1.单链表

定义

优点:不要求大片连续的空间,改变容量方便

缺点:不可随机存取,要耗费一定空间存放指针

代码定义一个单链表:

除此之外,当空间不够时,定义一个p指针,指向新开辟的结点

另外,这里struct LNode类型名的关键字很长,建议用 typedef 重定义一个类型名。比如:typedef struct LNode LNode; 就可以把上面的代码缩减成:LNode *p = (LNode*)malloc(sizeof(LNode));

使用Linklist强调的是单链表

使用LNode强调的是一个结点

初始化(带头结点):

  带头结点要比不带头结点的单链表,写代码更方便一些,头结点可以看做成第0个结点

  不带头结点,对第一个数据结点和后续数据结点的处理需要用不同的代码逻辑;对空表和非空表的处理需要用不同的代码逻辑

插入:
按位序插入(带头结点)

按位序插入(不带头结点)

找到第i-1个结点,将新结点插入其后。

考虑临界:

因为不存在“第0个”结点,因此i=1时需要特殊处理

前插操作

方法一:引入头结点

方法二:偷天换日

删除:

注意:删除之后要释放掉

按位序删除(带头结点)

代码:

考虑临界:

不带头结点,想要删除第1个节点,还要进行特殊处理

指定节点的删除

方法一:引入头结点

方法二:偷天换日(能把谁释放掉,就拷贝谁)

考虑临界:

当 p是最后一个结点,那上面的代码会出现bug,q指针指向NULL,此时访问q结点指向的data域里,会出现空指针的错误。

单链表的查找(带头结点)
按位查找

我们可以把这种,重复利用的操作封装成一个函数

封装(基本操作)的好处:避免代码重复,简洁、易维护

按值查找

时间复杂度

这两种时间复杂度都是O(n)

单链表的建立
尾插法

头插法

2.双链表

在单链表的基础上增加前驱结点 *prior

重命名和单链表类似

初始化(带头结点)

插入:

按位序插入和前插操作,道理相同,都是找到指定结点的前驱结点,进行后插操作

删除:

遍历

时间复杂度都是 O(n)

3.循环链表

特点:如果知道一结点,就可以知道其他任意一个结点

循环单链表 初始化:

在单链表的基础上,把最后一个头结点,指向头结点

  1. >next = L;

判断循环单链表是否为空,则需要判断头结点的next指针是否指向头结点本身

L->next == L;

判断节点p是否为循环单链表的标尾结点,则需要判断p结点指向的下一个结点是否为L

p->next == L;

循环单链表 头插,尾插

循环双链表 初始化:

表头的prior指针指向表尾结点,表尾的next指针指向头结点。

判断循环双链表是否为空,则需要判断头结点的next指针是否指向头结点本身

L->next == L;

判断结点p是否为循环双链表的表尾结点,则需要判断p的next是否指向L

p->next == L;

循环双链表 插入:

相比双链表,不需要考虑插入最后一个结点的情况,循环双链表的最后一个结点不指向NULL

循环双链表 删除:

同理,也不需要考虑末尾情况

4.静态链表

什么是静态链表?

静态链表:分配一整片连续的内存空间,各个结点集中安置

用数组的方式实现链表,游标(数组下标)充当指针。

定义一个静态链表

typedef 重定义

这里a表示,定义一个SLinkList类型的变量,SLinKList是一个数组,每个元素类型是struct Node

简述基本操作实现
初始化

查找,插入,删除

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值