线性表(linear-list)是最常用最简单的一种数据结构。一个线性表是n (n≥0)个相同类型数据元素的有限序列。记为: L= (a1, a2 , … , an )。
其中,L是表名,a1是第一个数据元素(也简称为首元素),无前驱,只有一个后继;an是最后一个数据元素(即第n个数据元素),只有一个前驱,无后继。其余的每个数据元素ai (i=2,3, … ,n-1)都只有一个前驱,且只有一个后继。i (i=1,2, … ,n)称为表中元素序号。n是数据元素的个数,也称为表的长度,若n=0,L称作空表。
线性表的顺序存储方式是指:用一组连续的有限空间依次存储线性表中的数据元素,简称为顺序表。
一块地址连续的空间存放线性表中的数据元素。任意两个逻辑上相邻的数据元素在物理上也必然相邻。
顺序表通常用数组存储,在C++中,数组有静态数组和动态数组两种,在此我们将采用动态数组方式存储。
通常顺序表的插入和删除操作都会保持各元素原来的顺序不变。
举例来看顺序表上的插入和删除。
在原来已有7个元素的表的第4个元素前插入数据元素x=24的过程。
顺序表是用数组方式来存储的,因数组元素个数固定。当顺序表的长度等于数组的元素个数时,顺序表就不能再插入新数据元素了。
对于有n个数据元素的顺序表,若要保持各数据元素原来的顺序不变,则插入和删除一个数据元素的时间复杂度为O(n)。
顺序表要求存储空间是物理上连续的,这样即使存储空间中的存储单位数超过所需的数目,却因其不连续,也无法使用。
克服这些缺陷的办法是:对线性表采用链表存储方式。
在链表存储方式中,用户通过new函数向系统动态申请所需的存储空间,把数据元素插入链表中合适的位置,而这些在不同时刻向系统动态申请的存储空间在内存中很可能不连续。
因而,任意两个在逻辑上相邻的数据元素在物理上不一定相邻,数据元素的逻辑次序是通过链表中的指针链接实现的。
链表的表长是动态的、可扩充的,在链表中插入和删除时不需移动元素。
用链表存储方式存储线性表数据元素的方法是用结点(node)构造链。结点通常有一个数据域,另外还有一个或一个以上的指针域。
链表存储主要有单链、单循环链和双向链等三种。这三种结构中每一种又有带头结点结构和不带头结点结构两种。
采用链接存储方式存储的线性表称为线性链表,又称单链表(linked list),或简称为链表。在单链表中,每一个数据元素占用一个结点。如图3-5所示。一个结点由两个域组成,一个域存放数据元素data,一个域存放指向该链表中下一个结点的指针next,它给出下一个结点的开始存储地址。
在单链表的表尾结点中,指针域为空以“”表示之。设线性表存有某系99级学生的学号如下:(99101,99104,99110,99201,99208),可用如图3-6所示的单链表表示。
单链表结构中又有带头结点和不带头结点两种结构。像图3-6这样的第一个结点就是数据结点的单链表结构,我们称其为不带头结点的单链表。
3-7是带头结点的单链表结构,头结点是由头指针所指向的不存放数据元素的结点。我们把头结点的数据域部分涂上阴影,以明显表示该结点为头结点。
单循环链表,简称循环链表(circular list), 是线性表的另一种链表表示,它的结点结构与单链表相同,由一个数据域和一个指针域组成,与单链表不同的是链表中表尾结点的next域中不是NULL,而是存放了head所指的结点。
在单循环链表中判断当前指针pcurrent是否到达链表尾的条件应是pcurrent->next == head。
单循环链表也有不带头结点和带头结点两种结构,分别如图3-14,图3-15所示。
对单循环链表,只要知道表中任一结点的地址,就能遍历表中其他任何结点。循环链表的运算与单链表类似,不过在链头与链尾处理时有所不同。