循环链表
循环单链表
循环单链表和单链表的区别在于,表中最后一个结点的指针不是NULL
,而改为指向头结点,从而使整个链表成环,如下图所示:
显然,在循环链表中没有指针域为NULL
的结点,因此,循环单链表的判空条件不是头结点的指针是否为空,而是它是否等于头指针L
。
循环双链表
与循环单链表类似,循环双链表中,尾结点的next
指针要指向头结点,头结点的prior
指针要指向尾结点。
如何判断一个循环双链表是空表呢?当其头结点的next
域和prior
域都等于头指针L
时。
静态链表
静态链表是用数组来描述线性表的链式存储结构,结点也有数据域data
和指针域next
,与前面所讲的链表中的指针不同的是,这里的指针是结点在数组中的相对地址也就是数组下标,又称游标。和顺序表一样,静态链表也要预先分配一块连续的内存空间。
总的来说,静态链表没有单链表使用起来方便。但对于一些不支持指针的高级语言(如Basic),这是一种非常巧妙的设计方法。
静态链表和单链表的对应关系如下图所示:
顺序表和链表的比较
存取(读/写)方式
- 顺序表可以顺序存取,也可以随机存取;
- 链表只能从表头开始依次顺序存取。
- 比如:顺序表访问第
i
个位置时仅需访问一次,而链表则需从表头开始依次访问i
次。
逻辑结构与物理结构
- 采用顺序存储时,逻辑上相邻的元素,对应的物理存储位置也相邻。
- 而采用链式存储时,逻辑上相邻的元素,物理存储位置不一定相邻,对应的逻辑关系是通过指针链接来表示的。
查找、插入和删除操作
- 对于按值查找,顺序表无序时,两者的时间复杂度均为O(n);顺序表有序时,可采用折半查找,此时的时间复杂度为O(logn)。
- 对于按序号查找,顺序表支持随机访问,时间复杂度为O(1);而链表的平均时间复杂度为O(n)。
- 顺序表的插入、删除操作,平均需移动半个表长的元素。
- 链表的插入、删除操作,只需修改相关结点的指针域即可。
空间分配
- 顺序存储在静态存储分配的情形下,一旦存储空间装满就不能扩充,若再加入新元素,则会出现内存溢出,因此需要预先分配足够大的存储空间。然而,预先分配过大,可能会导致顺序表后部大量闲置;预先分配过小,则可能会溢出。
- 顺序存储在动态存储分配情形下,虽然存储空间可以扩充,但需要移动大量元素,导致操作系统效率降低,而且若内存中没有更大块的连续存储空间,则会导致分配失败。
- 链式存储的结点空间只在需要时申请分配,只要内存有空间就可以分配,操作灵活、高效。此外,由于链表的每个结点都带有指针域,因此存储密度不够大。