在单循环链表中,虽然从任一单元出发,可以找到其前驱单元,但需要O(n)时间。如果我们希望能快速确定表中任一元素的前驱和后继元素所在的单元,可以在链表的每个单元中设置两个指针,一个指向后继,另一个指向前驱,形成图8所示的双向链表或简称为双链表。
图8 双链表
由于在双链表中可以直接确定一个单元的前驱单元和后继单元,我们可以用一种更自然的方式表示元素的位置,即用指向双链表中第i个单元而不是指向其前一个单元的指针来表示表的第i个位置。
双链表的单元类型定义如下。
type celltype=record element:TElement; next,previous:celltype; end; TPosition=^celltype; |
和单链的循环表类似,双链表也可以有相应的循环表。我们用一个表头单元将双链表首尾相接,即将表头单元中的previous指针指向表尾,并将表尾单元的next指针指向表头单元,构成如图9所示的双向循环链表。
图9 双向循环链表
下面仅以双向循环链表为例给出三种基本运算的实现。
(1)在双向循环链表L的位置p处插入一个新元素x的过程Insert可实现如下。
procedure Insert(x:TElement;p:TPosition;var L:TList); Var q:TPosition; begin new(q); q^.element:=x; q^.previous:=p^.previous; q^.next:=p; p^.previous^.next:=q; p^.previous:=q; end; |
上述算法对链表指针的修改情况见图10。
图10 在双向循环链表中插入一个元素
算法Insert中没有检查位置p的合法性,因此在调用Insert之前应保证位置p的合法性。由于插入通常是在表的头尾两端进行的,所以容易检查位置p的合法性。
(2)从双向循环链表L中删除位置p处的元素可实现如下:
procedure Delete(p:TPosition;var L:TList); begin if (p<>nil)and(p<>L) then begin p^.previous^.next:=p^.next; p^.next^.previous:=p^.previous; dispose(p^); end; end; |
上述算法对链表指针的修改情况见图11。
图11 从双向循环链表中删除一个元素
与单链表中的删除算法类似,上述算法是在已知要删除元素在链表中的位置p时,将该位置所指的单元删去。若要从一个表中删除一个元素x但不知道它在表中的位置,则应先用定位函数Locate(x,L)找出要删除元素的位置,然后再用Delete删除之。
(3)在双向循环链表中,定位函数Locate可实现如下。
function Locate(x:TElement;L:TList):TPosition; Var p:TPosition; begin p:=L^.next; while p<>L do if p^.element=x then return(p) else p:=p^.next; return(nil); end; |
算法Locate在最坏情况下需要的时间显然为O(n),n为表L的长度。算法Insert和Delete需要O(1)时间。在双向循环链表中,其他表的运算容易实现,这里就不一一说明了。另外,我们也可以用游标来模拟指针,从而实现双向链表和双向循环链表。
<script src="../../../lib/footer.js" type="text/javascript"> </script>