数据结构(四)顺序表与链表的深层次讲解

我们在数据结构(二),对链表和顺序表已经讲解过了。但很多同学表示有点晦涩难懂那我就出一篇深层次讲解,一步一步来带领大家学习。

我们从头(数据结构)开始完整的来为大家讲解,大家好好看好好学。定有收获

1.数据结构相关概念

1.1、什么是数据结构?

数据结构是由“数据”和“结构”两词组合⽽来。
什么是数据?

常⻅的数值1、2、3、4.....、教务系统⾥保存的⽤⼾信息(姓名、性别、年龄、学历等等)、⽹⻚⾥⾁眼可以看到的信息(⽂字、图⽚、视频等等),这些都是数据
什么是结构?
当我们想要使⽤⼤量使⽤同⼀类型的数据时,通过⼿动定义⼤量的独⽴的变量对于程序来说,可读性⾮常差,我们可以借助数组这样的数据结构将⼤量的数据组织在⼀起,结构也可以理解为组织数据的⽅式。
想要找到草原上名叫“咩咩”的⽺很难,但是从⽺圈⾥找到1号⽺就很简单,⽺圈这样的结构有效将
⽺群组织起来。
概念:数据结构是计算机存储、组织数据的⽅式。数据结构是指相互之间存在⼀种或多种特定关系
的数据元素的集合。数据结构反映数据的内部构成,即数据由那部分构成,以什么⽅式构成,以及数据元素之间呈现的结构。
总结:
1)能够存储数据(如顺序表、链表等结构)?
2)存储的数据能够⽅便查找?
2、为什么需要数据结构?


如图中所⽰,不借助排队的⽅式来管理客⼾,会导致客⼾就餐感受差、等餐时间⻓、餐厅营业混乱等情况。同理,程序中如果不对数据进⾏管理,可能会导致数据丢失、操作数据困难、野指针等情况。
通过数据结构,能够有效将数据组织和管理在⼀起。按照我们的⽅式任意对数据进⾏增删改查等操
作。最基础的数据结构:数组。


【思考】有了数组,为什么还要学习其他的数据结构?
假定数组有10个空间,已经使⽤了5个,向数组中插⼊数据步骤:?
求数组的⻓度,求数组的有效数据个数,向下标为数据有效个数的位置插⼊数据(注意:这⾥是
否要判断数组是否满了,满了还能继续插⼊吗).....?
假设数据量⾮常庞⼤,频繁的获取数组有效数据个数会影响程序执⾏效率。
结论:最基础的数据结构能够提供的操作已经不能完全满⾜复杂算法实现。


2.顺序表

2.1、顺序表的概念及结构?

2.1.1线性表

线性表( linear list )是n个具有相同特性的数据元素的有限序列。 线性表是⼀种在实际中⼴泛使
 ⽤的数据结构,常⻅的线性表:顺序表、链表、栈、队列、字符串...
 线性表在逻辑上是线性结构,也就说是连续的⼀条直线。但是在物理结构上并不⼀定是连续的,
线性表在物理上存储时,通常以数组和链式结构的形式存储。
案例:蔬菜分为绿叶类、⽠类、菌菇类。线性表指的是具有部分相同特性的⼀类数据结构的集合
如何理解逻辑结构和物理结构?

2.1.2顺序表分类

顺序表和数组的区别:

顺序表的底层结构是数组,对数组的封装,实现了常⽤的增删改查等接⼝。

顺序表分类:

2.2.1   静态顺序表

        概念:使⽤定⻓数组存储元素

我们来一点一点解释,在这串代码中我们使用了自定义的 typedef 我们把 int 重命名为 SLDateType

目的:是为了区分其他代码中的int ,  还有一个原因我们可以随时改变类型,在这个局部变量内部我们可以改变类型,假设我们想把int 类型改为char类型,我们直接修改即可。它不会影响程序内部其他的char,与int。

宏的作用:我们使用define来给N赋值,目的也很简单方便修改数组的大小。

静态顺序表缺陷:空间给少了不够⽤,给多了造成空间浪费 ------我们给的空间都是定义好的,没有空间去修改,给多少就是多少,不方便我们实际的使用。

   静态顺序表由于不实用我们在这里就着重讲一下动态顺序表。

好戏就要开始喽

2.2.2动态顺序表

我们来一步一步开始,首先我们创建好三个文件(一个头文件,两个源文件)。

这些文件有什么作用呢?

头文件:用来实现文件的声明与定义(定义顺序表要实现的结构\接口\方法等)

源文件:SeqList.c        具体实现接口和方法,实现程序的运行过程。

               text.c              实现测试作用,测试顺序表是否可以正常运行。

我们先从头文件开始

先实现静态顺序表

过程很简单和上面的一样

再来实现动态顺序表

我们接下来实现一下动态顺序表的 增加 删除 修改的功能。

初始化过程:着重详细讲解作为一个列子。

 我们在头文件中定义源文件中实现。

我们把空间都初始化为0;

初始化完成之后我们来进行测试(在text.c)文件中进行

我们创建了一个测试函数   slText01 用来实现。

当我们运行来试试是否初始化完成 ?

这是出了什么问题呢?

我们回头看看到底是哪出了问题

在传值的过程中我们会创建一个临时变量图中得到红色小方块,先把值放入这个临时创建的空间中然后完成调用

那么为什么会出现问题呢?

因为我们没有对sl 进行初始化(意味着sl是个无效值),我们没办法把值给拷贝下来放入临时创建的空间中传给s。

在这里我们就没办法进行传值操作了,只能进行传地址操作。

所以我们可以这样修改一下:

我们传地址然后使用指针来接收。

由于是指针类型我们不能继续使用点(.)来调用,而是使用指针类型的  ->  。

 当我们程序跑起来观察

程序没有问题,我们通过监视来看是否初始化成功

初始化之前:

初始化之后:

都变成了0,证明我们初始化成功。

我们对空间进行开辟(动态开辟)。数据的插入

头插             void SLPushFront(SL* ps, SLDataType x);

尾插              void SLPushBack(SL* ps, SLDataType x);

画图来进行分析:

 我们一个一个来讲解:

尾差

尾插有三种情况还是画图来展示:

大家肯定会有一个疑问,size不是有效数据的大小吗?在这里为什么是即将插入的下标呢?

其实size是有效元素的个数,但是也可以作为下一个即将插入元素的下标。

size就是下一个即将插入元素的下标,插入元素的时候直接用size作为下标,访问进行赋值
获取有效元素个数的时候,直接使用size。

扩容的原则:

关于动态内存管理我之前发布过专题博客很详细,感兴趣的可以看看,链接放下面了。

CSDNicon-default.png?t=N7T8https://mp.csdn.net/mp_blog/creation/editor/136505981

关于为什么扩容大多数都是扩容二倍这个问题,现在的我能力有限还无法为大家解释清楚,但是请大家相信我,有朝一日我肯定会弄明白。

那我们接着继续:

空间足够时:

直接插入:关于为什么是size,上面有解释这里就不过多说了

                          size++是怎么回事呢? 是因为当我们插入一个数据的时候,size(这里指的是有效数据)就会增加1,所以我们需要++。

空间不够时:

我们可以看到size现在指向的是6的位置,我们在它的后面进行扩容。

我们来观察一下这个代码有没有问题。

仔细观察是有问题的,因为我们在初始化capacity是给的是0,在这里二倍*0还是0.

无法达到扩容的效果。

我修改了一下,大家看一下是否还是后问题:

答案是还是有问题,因为在这里我们使用三目操作符给的值是4个比特位,但是我在前面定义的是int类型。我们可以这样修改一下。

我们申请之后还需要判断是否申请成功,我们先创建一个临时变量来存放,如果申请成功了,我们就赋给newCapacity,如果没有申请成功就返回失败。

这就是尾插的全过程,这里讲的比较详细比较照顾刚入门的同学。

好了代码已经写完了我们现在来测试一下:

 现在为大家展示测试结果:

以上就是尾插的全过程。

有了尾插作为基础后面的就很好理解了

我们接下来头插

头插

这里有个100,我们现在要把它通过头插的方式插入到第0的位置。该怎么操作呢?

我们把数据往后挪动,使100可以插入。那么数据的挪动是从前往后,还是从后往前呢?

我们假设一下:从前往后,0挪到1,那么此时1被覆盖很有可能导致数据丢失。

                      :从后往前,3挪到空位置,然后2挪到原先3的位置,不会使数据丢失。

所以我们使用从后往前进行挪动。

原理很简单,我们来展示代码。

 我们来测试一下:

有了这些知识,剩下的就好理解了。

下面把全部代码展示给大家

#define INIT_CAPACITY 4
typedef int SLDataType;
// 动态顺序表 -- 按需申请
typedef struct SeqList
{
SLDataType* a;
int size; // 有效数据个数
int capacity; // 空间容量
}SL;
//初始化和销毁
void SLInit(SL* ps);
void SLDestroy(SL* ps);
void SLPrint(SL* ps);
//扩容
void SLCheckCapacity(SL* ps);
//头部插⼊删除 / 尾部插⼊删除
void SLPushBack(SL* ps, SLDataType x);
void SLPopBack(SL* ps);
void SLPushFront(SL* ps, SLDataType x);
void SLPopFront(SL* ps);
//指定位置之前插⼊/删除数据
void SLInsert(SL* ps, int pos, SLDataType x);
void SLErase(SL* ps, int pos);
int SLFind(SL* ps, SLDataType x);

后续会接着为大家讲解数据结构相关知识

请大家持续关注

感谢你的观看。

  • 44
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值