*文中内容来源于《数据结构 --Java语言描述》(第二版) 刘小晶 杜选 主编
*此系列文章作为学习记录,若文中内容有误,请大家指出,谢谢
双向循环链表
循环链表
循环链表(Circular List)也称环形链表,其结构与单链表相似,只是将单链表的首尾相连,即将单链表的最后一个结点的后继指针指向第一个结点,从而构成一个环状链表。
在循环链表中,每一个结点都有后继,所以从循环链表的任一个结点出发都可以访问到单链表中的所有结点。循环链表的操作算法与单链表的操作算法基本一致,差别仅在于判断单链表中访问的是否是最后一个结点的条件不再是它的后继是否为空,而是它的后继是否为头结点。
要合并两个循环链表仅需将一个表的表尾和另一个表的表头相接即可。
合并过程中修改链的语句序列为:
Node p = tailb.next; //p指向第2个表的头结点
tailb.next = taila.next; //第2个表的表尾于第1个表的表头相连
taila.next = p.next; //第1个表的表尾与第2个表的首结点相连
双向链表
为克服单链表单一性的缺点,可对单链表进行重新定义,使其结点具有两个指针域,一个指针指向前驱结点,另一个指针后继结点。这种类型的链表成为双向链表。
双向链表也与单链表一样,只要首尾相连即可构成双向循环链表。在双向循环链表中存在两个环,他们分别是由前驱指针和后继指针连接而成的。
双向链表的结点类的描述
//双向链表的结点类描述
public class DuLNode {
public Object data; //存放结点值的数据域
public DuLNode prior; //存放指向前驱结点的指针域
public DuLNode next; //存放指向后继结点的指针域
public DuLNode(){ //无参数时的构造函数
this(null);
}
public DuLNode(Object data){ //构造数据域值为data的新结点
this.data = data;
this.prior = null;
this.prior = null;
}
}
双向循环链表类的描述
//在带头结点的双向循环链表上的插入和删除操作的算法中,无须专门判断和处理空表的情况;
//无须查找待插入位置或删除位置的前驱结点
import java.util.Scanner;
public class DuLinkList implements IList{ //接口IList在该代码块下方
public DuLNode head; //双向循环链表的头结点
//双向循环链表的构造函数,构造只含一个头结点的双向循环链表
public DuLinkList(){
head = new DuLNode(); //初始化头结点
head.prior = head; //初始化头结点的前驱和后继
head.next = head;
}
//从表尾到表头逆向创建双向循环链表的算法,其中n为该双向循环链表的结点个数
public DuLinkList(int n) throws Exception{
this();
Scanner sc = new Scanner(System.in); //构造用于输入的对象
for (int j = 0; j < n; j ++ ){
insert(0,sc.next()); //生成新结点,插入到表头
}
}
//在带头结点的双向循环链表中的插入操作
public void insert(int i, Object x) throws Exception{
DuLNode p = head.next; //初始化,p指向首结点
int j = 0; //j为计数器
while( !p.equals(head) && j < i){ //寻找插入位置i
p = p.next; //指向后继结点
++j; //计数器的值增加1
}
if (j != i && !p.equals(head)) //i不合法
throw new Exception("插入位置不合法"); //抛出异常
DuLNode s = new DuLNode(x); //生成新结点s
p.prior.next = s; //将新结点s插入的第i个结点p的前面
s.prior = p.prior;
s.next = p;
p.prior = s;
}
//带头结点的双向循环链表中的删除操作
public void remove(int i) throws Exception{
DuLNode p = head.next; //初始化,p指向首结点,j为计数器
int j = 0;
while( !p.equals(head) && j < i){ //寻找删除位置
p = p.next; //指向后继结点
++j; //计数器的值增加1
}
if (j != i) //i不合法
throw new Exception("删除位置不合法"); //抛出异常
p.prior.next = p.next; //修改指针,使第i个结点p从链中脱离出来
p.next.prior = p.prior;
}
//置空
public void clear(){
head.data = null;
head.prior = null;
head.next = null;
}
//判断双向循环链表是否为空
public boolean isEmpty(){
return (head.next == head || head.prior == head);
}
//读取带头结点的双向循环链表中第i个位置的数据元素
public Object get(int i) throws Exception{
DuLNode p = head.next;
int j = 0;
while( !p.equals(head) && j < i){
p = p.next;
++j;
}
if (j > i || p == head) { //i小于0或者大于表长减1时,即i不合法
throw new Exception("第" + i + "个元素不存在"); //抛出异常
}
return p.data; //返回结点p的数据域值
}
//读取当前双向循环链表的长度
public int length(){
DuLNode p = head.next; //初始化,p指向首结点,length为计数器
int length = 0;
while (p != head){ //从首结点开始向后查找,直到p为空
p = p.next; //指向后继结点
++length; //长度加1
}
return length;
}
//用户输入一个值,在双向循环链表中返回该值的位置
public int indexOf(Object x){
DuLNode p = head.next; //初始化,p指向首结点,j为计数器
int j = 0;
//下面从首结点开始查找,直到p.data为x或者到达head
while (p != head && !p.data.equals(x)){
p = p.next; //指向下一个结点
++j; //计数器的值增加1
}
if (p != head)
return j; //返回值为x结点在单链表中的位置
else
return -1;
}
public void display(){
DuLNode node = head.next; //取出带头结点的双向循环链表的首结点
while(!node.equals(head)){
System.out.print(node.data + " "); //输出结点的数据域
node = node.next; //取下一个结点
}
System.out.println();
}
}
补充:IList的描述
public interface IList {
public void clear();
public boolean isEmpty();
public int length();
public Object get(int i) throws Exception;
public void insert(int i, Object x) throws Exception;
public void remove(int i) throws Exception;
public int indexOf(Object x);
public void display();
}