- 1、单项循环列表
单向循环链表是单链表的另一种形式,其结构特点是链表中最后一个结点的指针不再是结束标记,而是指向整个链表的第一个结点,从而使单链表形成一个环。和单链表相比,循环单链表的长处是从链尾到链头比较方便。当要处理的数据元素序列具有环型结构特点时,适合于采用循环单链表。
- 2、单向循环链表
和单链表相同,循环单链表也有带头结点结构和不带头结点结构两种,带头结点的循环单链表实现插入和删除操作时,算法实现较为方便。带头结点的循环单链表结构如下:
带头结点的循环单链表的操作实现方法和带头结点的单链表的操作实现方法类同,差别仅在于:
(1)在构造函数中,要加一条head.next = head 语句,把初始时的带头结点的循环单链表设计成图2-11 (a)所示的状态。
(2)在index(i)成员函数中,把循环结束判断条件current != null改为current != head。
- 双向循环链表
双向链表是每个结点除后继指针外还有一个前驱指针。和单链表类同,双向链表也有带头结点结构和不带头结点结构两种,带头结点的双向链表更为常用;另外,双向链表也可以有循环和非循环两种结构,循环结构的双向链表更为常用。
在双向链表中,每个结点包括三个域,分别是element域、next域和prior域,其中element域为数据元素域,next域为指向后继结点的对象引用,prior域为指向前驱结点的对象引用。
如下图是带头结点的循环双向链表的图示结构。循环双向链表的next和prior各自构成自己的循环单链表。
在双向链表中,有如下关系:设对象引用p表示双向链表中的第i个结点,则p.next表示第i+1个结点,p.next.prior仍表示第i个结点,即p.next.prior == p;同样地,p.prior表示第i-1个结点,p.prior.next仍表示第i个结点,即p.prior.next == p。下图是双向链表上述关系的图示。
循环双向链表的插入过程如下图所示。图中的指针p表示要插入结点的位置,s表示要插入的结点,①、②、③、④表示实现插入过程的步骤。
//==========================================我是代码的分割线
//具体代码的分析已经详细的写在了解析里面,欢迎讨论
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * 本项目主要是通过几个类来组合实现 3 * 1、List的线性表的接口 4 * 2、Node节点类的具体方法 5 * 3、单项链表类的具体实现 6 */ 7 8 9 /** 10 * 线性表接口 11 */ 12 public interface List { 13 14 // 获得线性表的长度 15 public int size(); 16 17 // 判断是否为空 18 public boolean isEmpty(); 19 20 // 插入操作,需要抛出异常 21 public void insert(int index, Object obj) throws Exception; 22 23 // 删除元素 24 public void delete(int index) throws Exception; 25 26 // 获取指定位置元素 27 public Object get(int index) throws Exception; 28 29 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//节点类 //这个是重点!!!!Java一样可以实现指针的跳转->使用引用 public class Node { Object element; // 数据域 Node next;// 后继指针域 Node prior;// 前驱指针域 // 头节点的构造方法 public Node(Node nextval) { this.next = nextval; } // 非头节点的构造方法 public Node(Object obj, Node nextval) { this.element = obj; this.next = nextval; } // 获得当前节点的后继结点 public Node getNext() { return this.next; } // 获得当前节点的前驱结点 public Node getPrior() { return this.prior; } // 获得当前节点的数据域的值 public Object getElement() { return this.element; } // 设置当前节点的后继指针域 public void setNext(Node nextval) { this.next = nextval; } // 设置当前节点的前驱指针域 public void setPrior(Node priorval) { this.prior = priorval; } // 设置当前节点的数据域, public void setElement(Object obj) { this.element = obj; } public String toString() { return this.element.toString(); } }
1 //单向链表类 2 public class DoubleCycleLinkList implements List { 3 4 Node head;// 头指针 5 Node current;// 当前节点对象 6 int size; // 节点的个数 7 8 // 初始化一个空链表 9 public DoubleCycleLinkList() { 10 // 初始化头节点,让头指针指向头节点,并且让当前节点对象等于头节点 11 this.head = current = new Node(null); 12 this.size = 0; // 单项链表出事长度为0 13 // 循环列表属性 14 this.head.next = head; 15 this.head.prior = head; 16 } 17 18 // 定位函数,实现当前操作对象的前一个节点,也就是让当前节点对象定位到要要操作节点的前一个节点 19 public void index(int index) throws Exception { 20 // 参数非法时,出现-1是由于第一个元素的下标是0,而第一个元素的前面就是头节点,该节点就得用-1表示下标了。 21 if (index < -1 || index > size - 1) { 22 throw new Exception("参数错误"); 23 } 24 // 说明是在头节点之后进行操作 25 if (index == -1) 26 return; 27 // 通过该循环获得要操作的那个数的前一个节点 28 current = head.next; 29 int j = 0;// 循环变量 30 // 循环链表属性 31 while (current != head && j < index) { 32 current = current.next; 33 j++; 34 } 35 36 } 37 38 @Override 39 public int size() { 40 // TODO Auto-generated method stub 41 return this.size; 42 } 43 44 @Override 45 public boolean isEmpty() { 46 // TODO Auto-generated method stub 47 return size == 0; 48 } 49 50 @Override 51 public void insert(int index, Object obj) throws Exception { 52 // TODO Auto-generated method stub 53 // 可以等于0表示在一个元素的前面插入,也就是头结点的后面 54 if (index < 0 || index > size) { 55 throw new Exception("参数错误! "); 56 } 57 /** 58 * 这里是核心操作 59 */ 60 // 定位到要操作节点的前一个节点对象 61 index(index - 1); 62 // 将前一个节点进行设置,使其指向要插入节点的指针域, 63 current.setNext(new Node(obj, current.next)); 64 size++; 65 } 66 67 @Override 68 public void delete(int index) throws Exception { 69 // TODO Auto-generated method stub 70 if (isEmpty()) { 71 throw new Exception("链表为空,无法删除"); 72 } 73 74 if (index < 0 || index > size) { 75 throw new Exception("参数错误!"); 76 } 77 // 进行定位 78 index(index - 1); 79 // current此时表示要删除节点的前一个节点的指示,因此需要将该节点指向要删除的那个节点的下一个节点才对 80 // 修改后继指针 81 current.setNext(current.next.next); 82 // 修改前驱指针 83 current.next.setPrior(current); 84 size--; 85 86 } 87 88 @Override 89 public Object get(int index) throws Exception { 90 // TODO Auto-generated method stub 91 if (index < -1 || index > size - 1) { 92 throw new Exception("参数非法,无法查询"); 93 } 94 // 先定位 95 index(index); 96 return current; 97 } 98 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//测试类 public class Test { public static void main(String[] args) throws Exception { DoubleCycleLinkList list = new DoubleCycleLinkList(); for (int i = 0; i < 10; i++) { // 0-99之间的整数 int temp = (int) (Math.random() * 100); list.insert(i, temp); System.out.print(temp + " "); } list.delete(4); System.out.println(); System.out.println("----删除第五个元素之后------"); for (int i = 0; i < list.size; i++) { System.out.print(list.get(i) + " "); } } }