链表(linked list):一种数据结构,其中的对象按线性顺序排列,与数组类似,但不同的是,数组按下标排序,而链表顺序由指针决定。
链表有多种形式:单/双链接,已/未排序,是/否循环。本文讨论未排序的双链接链表。
双向链表(doubly linked list)L中的每一个元素都是一个对象,通常有三个属性:一个关键字key(储存的数据),两个指针:next与prev。当然,也可以包括其他的辅助数据(或称卫星数据)。
对于每一个元素x,x.prev指向前驱元素,x.next指向后继元素。
x.prev=NULL时,x为链表的头(head);x.next=NULL时,x为链表的尾(tail)。L.head指向链表第一个元素,若L.head=NULL,则链表为空。
函数原型:
list-search(L, k):
x = L.head
while x!=NULL && x.key!=k:
x = x.next
return x
list-insert(L, x)
x.next = L.head
if L.head != NULL
L.head.prev = x
L.head = x
x.prev = NULL
list-delete(L, x):
if x.prev!=NULL:
x.prev.next = x.next
else L.head = x.next
if x.next!=NULL
x.next.prev = x.prev
哨兵:一个用来简化边界条件的哑对象。L.null代表NULL,但也拥有和其他对象相同的各个属性。
使用哨兵后的函数原型:
list-search(L, k):
x = L.null.next
while x!=L.null && x.key!= k:
x = x.next
return x
list-insert(L, x):
x.next = L.null.next.prev
L.null.next.prev = x
L.null.next = x
x.prev = L.null
list-delete(L, x):
x.prev.next = x.next
x.next.prev = x.prev
哨兵基本不能降低数据结构相关操作的渐进时间界,但是可以降低常数因子。在循环语句中使用哨兵的好处往往是使代码简介,而非提高速度。
并且,如果有许多个很短的链表,使用哨兵反而会浪费大量储存空间资源。
显然,链表的delete操作并非真正地把某个对象删除,只是把它踢出了链表而已,实际上这个对象仍旧存在。
事实上,虽然常用结构体、类、对象这类数据类型储存链表,当程序语言没有这类数据类型时,可以用单数组或多数组的形式储存链表,且可以在单数组中通过free自由表分配和释放链表元素,即便自由表与链表交错仍然可行。所以,数据结构是很灵活的,并非一成不变。