2.3.3循环链表
循环链表时另一种形式的链表,它的特点时表中最后一个结点的指针域不再为空,而是指向表头结点,整个链表形成一个环.由此,从表中任一结点出发均可找到链表中其他结点.
循环链表的操作与单链表基本一致,差别仅在于算法中判断到达表尾的条件不是p或p.next是否为空,而是它们是都等于头指针.
使用单循环链表的结构时,为了找到最后一个结点,必须从表头head去访问每一个结点,而从循环链表的任一结点出发均可找到链表中其他结点,因此可改变单循环链表的标识方法,不用头指针,而用一个指向表尾的结点的尾指针rear来标识,这样从尾结点的指针域立即可以得到表头结点的地址,无论是查找第一个结点,还是查找最后一个结点都很方便.
例如,对两个单循环链表H1,H2的链表操作,将H2的第一个数据结点链接到H1的尾结点,如果用头指针标识,则需找到第一个链表的尾结点,其时间复杂度为O(n),而用尾指针rear1,rear2来标识,则时间复杂度为O(1).
链接过程如下:
2.3.4双向链表
之前所讨论的链表,结点中只有一个指向后继结点的指针域next,若已知某个结点,要找其前驱结点,则只能从表头指针出发.找后继结点的时间复杂度为O(1),而找前驱的时间复杂度为O(n);
如果希望找前驱的时间复杂度也为O(1),那就需要付出空间的代价,在每个结点中再设一个指向前驱的指针域,这种结点组成的链表称为双向链表,结点结构如下:
1.双向链表的存储结构描述
public class DuNode<T>{
T data;
DuNode<T> prior;
DuNode<T> next;
public DuNode(DuNode<T> n) {
next = n;
prior = null;
}
public DuNode(T obj,DuNode<T> p,DuNode<T> n) {
data=obj;
prior=p;
next=n;
}
}
2.双向循环链表
在双向链表中,若变量p引用了某个结点,则显然有:
p.next.prior=p.prior.next
在双向链表中,有些操作,如size,value,find等仅需涉及一个方向的指针,则它们的算法描述和单链表的操作相同,但在插入,删除时,双向链表中需要同时修改两个方向上的指针.
双向链表结点的插入
操作如下:
s.prior=p.prior;
p.prior.next=s;
s.next=p;
p.prior=s;
双向链表中结点的删除
设p引用双向链表中某结点,删除该结点时,其删除过程如下:
操作如下:
p.prior.next=p.next;
p.next.prior=p.prior;