【数据结构】《线性表》之 顺序存储结构和链表存储结构

数据结构中的线性表 是理解图,堆栈,二叉树的基础,他的官方定义为:

线性表 是 零个或多个数据元素的有限序列。

比如:a1, a2, a3 ,a4, ...... , ai-1, ai, ai+1,....., an

ai-1 是ai的前驱元素,而ai+1是ai的后继元素。我们可以得知,当i=2, ...., n-1时,他们只有唯一的一个前驱元素和后继元素。

并且,在线性表中,a1~an所代表的元素必须是相同的数据类型的元素。(比如a1-an代表有n个不同类型的人,但他们都是人,你不能在其中添加一个帽子的存储)。

线性表在物理结构上可以分为:顺序存储结构和链表存储结构。


  • 第一节:首先我们了解下顺序存储结构:

顺序存储结构就是在内存空间中开辟一片连续的空间,然后把数据按照顺序进行存储的一种方式

他包含三个属性:1、存储空间的起始位置(也就代表我们定义了一个数组)2、最大的存储容量(数组最大长度)3、线性表的当前长度

属性2和3的区别是:数组的长度是基本不变的,这是我们在申请内存空间的时候就已经确定好的,而我们的线性表的长度是代表着元素个数,是不确定的长度。则两者的关系为: 线性表的当前长度<=数组长度;

1 顺序存储结构的插入与删除:

1.1、插入思路:

①、我们首先需要考虑异常(插入位置异常,插入后的长度异常等)

②、从最后一个元素遍历到插入位置,分别将每一个元素向后移动一个位置;

③、插入目标元素,表长加1;

1.2、删除思路:

①、我们仍然需要首先考虑异常(删除位置错误等)

②、查找到需要删除的位置,遍历将其后的每一个元素向前进行移动。

2  总结

我们可以看出,在插入算法中,顺序存储结构中元素在插入位置的过程中,多数元素都需要进行移动,来给插入的元素腾位置。并且,在删除算法中也是类似的道理。我们计算下他们的时间复杂度:顺序存储结构在读取数据的时候,因为可以按照list[index]进行读取,所以时间复杂度为O(1),但在插入和删除算法的时候,平均的时间复杂度为O(n);

我们可以看出顺序存储结构的优点和缺点:

优点是:不需要为表示元素之间的逻辑关系而增加额外存储空间,可以快速的存取表中的任一位置的元素。

缺点是:插入和删除操作需要移动大量的元素。当线性表变化较大的时候,难以确定存储空间的容量。


第二节:接下来我们介绍下链式存储结构(相对重要):

不像顺序存储结构类似于站成一排进行排队,而是相当于每个人都会带着自己将要去哪里的信息在内存空间中随机分布。

如下图所示,每个结点(Node)包含数据域(存在数据)以及指针域(用于存放下一个结点的位置信息)。

单链表是由很多个结点组成的,一个完整的单链表应该包括头结点,以及尾结点(NULL)。



我们可以从下图看出,设第一个节点是P,则数据元素ai为p->data, 而P的下一个节点是p->next. 下一个元素的数据为P->next->data .



单链表的读取思路(注意与顺序存储结构进行对比):

1. 声明一个指针p指向链表的第一个节点,并且初始化工作计数j.

2.进行循环while(j<i),指针不断后移,指向下一个节点,直到j=i,查找成功。这个时候取出数据P->data.

我们可以看出链表的结构中没有定义表长,所以我们无法使用for来控制循环,单链表读取的核心就是:工作指针后移查找


单链表的插入和删除:

我们在顺序存储结构中已经看到,因为每个数据元素都是紧密相接,所以当我们进行操作的时候就会牵一发而动全身。但是对于链表来说,数据元素本来就是不是在一起的,所以我们可以通过改变要插入位置的前后联系关系来进行操作。这样就可以惊动最少的数据元素来插入或者删除数据。于黑夜中杀人于无形~

如上图所示,当我们想要在ai和ai+1中插入数据的时候,我们只需要让ai知道自己的下一位目标是s就可以了,然后让s知道自己的下一位目标是ai+1. 这样的话,链表的插入操作就会变得很简单:s->next=p-next; p->next=s; (这两条语句千万不能写反了,要注意下为什么哟?)

而我们的链表删除就会变得更加的简单,我们只需要去掉中间的ai结点就可以了。

p->next=p->next->next;


单链表的整表创建和删除:

我们经过上面的知识也就知道了,顺序存储结构的创建是声明一个类型大小确定的数组并且进行赋值。

而链表的是一种动态结构,创建链表的过程就是一个动态生成链表的过程,(空表-建立结点,插入结点)

而链表的创建过程分为两种思路:

第一种,头插法:

①、声明指针变量等,初始化链表*L。

②、让L的头指针指向NULL,这样我们就创建了一个只有头指针和尾指针(NULL)的链表。

③、接下来的问题就变成了,循环生成空结点,随机赋值,并且使用上面的插入的办法将结点插入到头结点和第一个头结点之间。

第二种,尾插法:

①、声明指针变量,初始化链表*L等;

②、我们需要定义一个尾部指针,随着链表的创建不断地将尾部指针后移操作。

③、当生成新的结点的时候,我们需要把尾部指针的下一个结点指向新生成的结点,然后将新生成的结点赋值给尾部指针,用于下一次的循环迭代。

④、结束的时候将尾部指针指向NULL .

单链表的整表删除:

①、声明两个指针变量p,q.一个用于工作指针,一个用于临时存储信息。

②、把第一个结点赋值给p,将下一个结点赋值给q,q=p->next ;然后释放p,接着p=q.接着进行循环。


总结:


接下里就是我们实际练习的部分:我会从剑指offer或者leetcode上寻找一些链表的经典题目,然后进行编程练习操作:

xxxx(待补充链接)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值