单行循环链表
概念:单向,都知道,上一个博客中也介绍了单链表的概念,循环也很好理解,将单链表实现首尾相连。即尾节点的指针域存储的是首节点的地址。
因为循环链表(LoopList)还是属于线性表,因此我们与单链表一样,可以定义一个LoopList类,继续让它实现接口List<E>,并实现当中的方法,与单链表中一样,我们还是一样,继续定义一个内部类Node,并且用户不可访问,只是通过LoopList使用循环链表。下图为单向循环链表。
1、先创建一个内部类Node,并且与之前一样,继续有data域和next指针域。便于链表节点之间的链接。
private class Node {
E data;// 数据域
Node next;// 指针域
public Node() {
this(null, null);
}
public Node(E data, Node next) {
this.data = data;
this.next = next;
}
@Override
//重写toString方法,以便于输出节点信息。
public String toString() {
return data.toString();
}
}
2、获取链表的长度。getSize()方法。
public int getSize() {
//size为之前定义的size用于存储链表的长度
return size;
}
3、判断链表是否为空,这也是比较重要的一步,在删除元素,插入元素等功能时也起着作用。isEmpty();
public boolean isEmpty() {
return size == 0 && head == null && rear == null;
}
4、接下来就应该给链表中添加元素了,也是有点困难的一步。在单链表中我们设置头节点为虚拟头节点,但是我们在循环链表中,应该注意一下,因为是循环链表,为了之后的尾指针便于和头指针相连,所以我们设置为真实头结点。刚开始时,头节点和尾节点都限制为空。知道有一个元素进来时在移动。
(1)链表为空时,我们插入元素时,应注意,当第一个元素来时,应当将头尾指针都指向这个节点,之后将为指针再指向头指针。
if (isEmpty()) {//判断是否为空若为空证明时空链表。
head = n;
rear = n;
rear.next = head;
}
(2)头插法,先将头节点的地址,给这个新结点,然后将该新结点的地址给头指针.最后将尾指针的节点的指针域指向该链表头节点。
if (index == 0) {// 头插
n.next = head;
head = n;
rear.next = n;
}
(3)尾插法,将新结点的next域指向头节点,然后将尾指针指向的节点的next域指向这个新结点,最后将尾指针指向尾指针的后继。
if (index == size) {// 尾插
n.next = rear.next;
rear.next = n;
rear = rear.next;
}
(4)一般插法,与单链表中的一般插入发一样,不用考虑头尾指针,先通过循环找到待插位置,然后再进行插入。
{
Node p = head;
for (int i = 0; i < index - 1; i++) {
p = p.next;
}
n.next = p.next;
p.next = n;
}
(5)当然,在最后执行完插入后,size要加1.
5、删除元素,remove(int index);
(1) 删除角标合法时,我们要判断删除元素的位置,如果是删除的链表只有一个元素时,直接将头尾指针都指向空就可以了。
if (size == 1) {
res = head.data;
head = null;
rear = null;
}
(2)删除头结点时,将头指针后移到当前头指针的后继节点,之后将尾指针指向当前头节点。
if (index == 0) {
res = head.data;
head = head.next;
rear.next = head;
}
(3)删除尾结点时,因为没有角标,所以必须用循环找到尾结点的前驱节点p,之后将当前尾指针的后继结点的地址,也就是头节点的地址给p.next,最后将为指针往前移就可以了,就是指向p当前的节点。
if (index == size - 1) {
res = rear.data;
Node p = head;
while (p.next != rear) {
p = p.next;
}
p.next = rear.next;
rear = p;
}
(4)一般删除,通过待删节点的角标,找到待删节点的前驱节点,之后将带删节点的后继节点的地址给当前带删节点的前驱节点,就已经达到删除操作了,当然也可以将删除元素的next置空。不过也不影响。
{
Node p = head;
for (int i = 0; i < index - 1; i++) {
p = p.next;
}
Node del = p.next;
res = del.data;
p.next = del.next;
del.next = null;
}
(5)最后将链表的长度减1。
6、还有一个clear的功能,就是将头尾指针都置为null;然后将链表长度置为0;
至此,循环链表功能也基本实现了。