今天来讲一讲数据结构中基础的线性表。可以说,线性表是数据结构与算法的基础。
线性表的定义:有限的数据元素组成的有限序列。
线性表有两种存储结构:1.顺序存储结构(数组)。2.链式存储结构(链表)
1.顺序存储结构:指用一段连续的存储单元依次存储数据元素的结构。
顺序存储结构的地址计算:
因为顺序存储结构是连续的一段存储单元,那我们假设每一个存储单元的大小为‘c’
那么可以推算出第ai个存储单元和第ai+1个存储单元的位置关系是:Location(ai) + c = Location(ai + 1)
如果我们知道这一段存储单元的第一个位置Location(a1),那么就可以推算出顺序存储结构中的所有存储单元的位置:Location(ai) = Location(a1) + c * (ai - 1)
实现(python):包括(创建、插入与删除、删除整表)
线性表顺序存储结构的优劣:
优点:1.查找数据元素时的时间复杂度都是O(1)。2.无需为数据元素之间的逻辑关系而耗费新的空间。
缺点:1.在进行增加元素、删除元素时、时间复杂度时O(n),不方便数据元素的增删。2.需要在创建时确定长度,在实际项目中很多时候在一开始很难确定线性表的大小。3.需要占用整块连续的内存空间,容易造成存储空间的碎片化。
2.线性表的链式存储结构:
链式存储结构基本存储单元:结点(Node)。、
结点:由两部分组成,1.数据域(用来存放数据的存储空间)。2.指针域(存储下一个结点或上一个节点的地址的存储空间)
python实现:
在构造函数中的val即是数据域、p即使指针域。
链表:链表才是实现线性表的数据结构,链表的组成单元是结点。
单向链表的必须要素:头指针
其实一开始看书我一直没有明白头指针的含义,后来通过自己的实操才理解头指针的意思就是指向第一个存储数据元素的结点的结点,只是这个节点的数据域可以为空。
这就是一个头指针简单的定义,因为我在结点的构造函数里传入了p=None,所以这里不需要再次定义指针域。
头指针的意义:即是一个规定也可以方便链表的插入和查找。
给链表添加元素的方法:
插入删除方法:
链表的读取:
链表的删除:
以上就是单链表的一些基础功能,那么单链表构成的线性表和顺序存储结构有什么优劣呢?
优势:1.单链表是动态存储结构,不需要事先分配好存储空间,大小不受限制,而且可以灵活利用内存的存储空间。2.单链表插入和删除的处理时间复杂度为O(1),比顺序存储结构领先了两个级别。
劣势:1.单链表元素查找的时间复杂度为O(n)。
综上所述,当创建的线性表需要用来频繁查找,很少进行插入和删除之类的元素修改工作,可以考虑使用顺序存储结构,例如:用户的个人信息,除了在创建时进行插入操作之外,更多的是进行读取每次登陆调用,可以用顺序存储结构。当存储的数据元素需要进行频繁的增加或删除之类的改动,可以使用链式存储结构。例如:游戏中玩家的武器装备列表,需要在游戏中频繁的进行修改,可以使用链式存储结构。再或者,当你清楚自己需要存入的是什么东西时,大小确定的时候,可以用顺序存储结构,如果不清楚存入的大小,那肯定是用链式存储结构了。
2.静态链表:
静态链表本质和顺序存储结构相同,都是需要开辟一段连续的存储空间。不同的地方在于静态链表中的每个存储单元除了需要存放数据之外,还需要向链表一样存储下一个存储空间的地址。
如图所示,静态链表的第一个空间存储的是存储空间中的第一个空的空间的地址,最后一个单元存储的是空间中第一个存放数据的地址。
比如说当我们想在乙的后面插入一个丙要怎么做呢?
1)把丙的值赋给第一个空间存储的地址7。 2)把丙的存储空间的下一个地址改为乙的下一个,即是4。 3)把乙的存储空间的中的存储地址改为7,即改为丙的存储空间。4)最后静态链表的第一项的地址改为8。
总结:静态链表是为了给没有指针定义的高级语言所设计的一种能够实现链表功能的方法。但现在绝大部分语言都可以使用指针或虚拟指针,静态链表使用较少。
3.循环链表:
实现:把链表中的最后一个节点指向头节点,即实现了循环链表。
循环链表的合并:合并两个循环链表可以使用尾指针,把第一个尾指针的下一个结点指向下一个链表的尾指针的下一项的下一项,把第二条链表的尾指针的下一项指向第一条链表的头指针的下一项,既实现了两个循环量表的合并。
4.双向链表:
实现:在定义结点的时候增加一个前指针pre,每一次添加结点时,都需要实现下一个指针、和前一个指针。
双向链表:
添加元素的方法:
以上便是线性表的两种结构及多种实现方法,本人是个菜鸡,希望大家发现问题了能够多多指正,交流。