考点(线性表是算法题命题的重点)
这类算法题实现起来比较容易且代码量较少,但是要求具有最优的性能(时间复杂度、空间复杂度,才能获得满分。因此,应固掌握线性表的各种基本操作(基于两种存储结构),在平时的学习中多注重培养动手能力。另外,需要提醒的是,算法最重要的是思想!考场上的时间紧迫,在试卷上不一定要求代码具有实际的可执行性,因此应尽力表达出算法的思想和步骤,而不必过于拘泥每个细节。
注意算法题只能用 C/C++语言实现。
线性表是具有相同数据类型(值及包含的操作)的n个数据元素的有限序列(n为表长,n=0代表线性表是一个空表)。表示为
线性表有下面两条逻辑特性:
线性表有下面五条特点:
线性表基本操作(其它操作都需要调用基本操作来完成)
下面要总结的是基于两种存储结构(顺序结构和链式结构)实现的线性表,需要注意的是线性表是一种逻辑结构,顺序结构和链式结构是存储结构。
用一组地址连续的存储单元依次存储线性表中的数据元素,从而使得逻辑上相邻的两个元素在物理位置上也相邻。
下表第i个元素后面紧接着存储第i+1个元素,其中i称为元素ai在线性表的位序。
通过数组定义顺序表,而一位数组可以是静态分配也可以是动态分配的
# define InitSize 100 // 表长度初始定义
type struct{
ElemType *data;
int MaxSize, length;
}
在线性表的第i个位置插入数据元素e。若i输入不合法,则返回False。 否则将第i个元素后面的所有元素依次移动一个位置,将元素e插入位置i
注意这里i指的是位序(1到n)
时间复杂度:
删除顺序表L中第i个位置的元素。若i的输入不合法,返回False。否则,将被删元素赋予变量e,并将第i+1个元素及后面元素向前移动一格,返回True。
bool ListDelete(SqList &L, int i, Elemtype &e){
if(i<1 || i>L.length) return false;
e=L.data[i-1];
for(int j=i; j<L.length; j++){
L.data[j-1] = L.data[j];
L.length--;
return true;
}
}
由于顺序表有随机存取的特点,因此按序查找时间复杂度是O(1)
链式存储线性表时,不需要使用地址连续的存储单元,即不要求逻辑上相邻的元素在物理位置上也相邻,它通过“链”建立起元素之间的逻辑关系,因此插入和删除操作不需要移动元素,而只需修改指针。但也会失去顺序表可随机存取的优点。
链表分为单链表、双链表、循环链表、静态链表
通过一组任意的存储单元来存储线性表中的数据元素,每个元素结点/存储单元都包含元素信息和指向下一个元素的指针,以此建立元素之间的线性关系。
单链表结点如上图所示,data为数据域存放元素数据,next为指针域存放后继结点的位置。
单链表的结点类型描述
区分头结点和头指针
如图2.4,头指针指的是指向第一个结点的指针,头结点指的是带头链表中第一个结点(通常带有存储信息)。
头结点带来的两个好处
从单链表头结点出发,顺着next指针搜索位置i的结点,如果没有则返回False
时间复杂度是O(n)
从单链表第一个开始,顺着next依次比较值。若某结点的数据域的值等于给定值返回指向该结点的指针,否则返回False。
时间复杂度是O(n)
插入操作的时间主要耗费在寻找位序,复杂度为O(n)。而插入仅仅为O(1)
计算单链表中数据结点(不含头结点)的个数。从第一个结点开始依次查找每个结点,同时设置一个计数器,每访问到一个结点,计数器加1,直至访问结点为空。时间复杂度为O(n)。
需要注意的是计算表长不算头结点。
双链表中存在两个指针——prior(指向前驱结点)和next(指向后继结点)
结点类型描述:
typedef struct DNode{
ElemType data;
struct DNode *prior, *next;
}DNode, *DLinkList;
双链表只是增加了一个前驱结点,因此按值查找和按位查找操作与单链表一致,但是插入和删除有了较大的改变。
注意顺序不一致可能导致出错
循环单链表和单链表的区别在于,表中最后一个结点的指针不是 NULL,而改为指向头结点从而整个链表形成一个环。分为循环单链表和循环双链表。
表尾结点的next指向不再为空,而是表头结点。因此,循环单链表判空的条件不再是头指针指向是否为空,而是指向是否是头指针自身。
一般情况下,对循环单链表只设置表尾指针。原因是,若设的是头指针,对在表尾插入元素需要 O()的时间复杂度,而若设的是尾指针r,r->next 即为头指针,对在表头或表尾插入元素都只需要 O(1)的时间复杂度。
与双链表对比不同的是表头结点的前驱结点指向表尾结点,表尾结点的后继结点指向表头结点
静态链表借助数组来表达链式结构,如下图
结点也存在数据域和指针域,指针域存储的是下一个结点在数组中的地址(相对地址),称为游标。
需要注意的是静态链表也需要分配一个连续的内存空间,且以-1作为不存在下一个结点的标志。
单链表结点中只有一个指向其后继的指针,使得单链表只能从头结点依次顺序地向后遍历。要访问一个结点时间复杂度为O(n)。双链表改进了上述问题。
循环单链表一般仅设尾指针,以使得操作效率更高。原因是,若设的是头指针,对在表尾插入元素需要 O(n)的时间复杂度,而若设的是尾指针 r,r->next 即为头指针,对在表头或表尾插入元素都只需要 O(1)的时间复杂度。