1,单向循环链表
- 单向循环链表就是尾节点的next指向头节点
- 当循环链表只有一个节点的时候,next指向自己
1.1,插入方法
public void add(int index, E element) {
rangeCheckForAdd(index);
if (index == 0) {
// 创建新节点, 并指向原链表的first节点
Node<E> node = new Node<>(element, first);
// 取出最后一个节点
Node<E> last = size == 0 ? first : node(size - 1);
// 最后一个节点的next指向新节点
last.next = node;
// first指针指向新节点
first = node;
}else {
Node<E> prev = node(index - 1);
prev.next = new Node<>(element, prev.next);
}
size++;
}
- 插入方法在index==0时与之前单向链表不同,需要把尾节点的next指向插入的新节点
1.2,删除元素
public E remove(int index) {
Node<E> node = first;
if (index == 0) {
if (size == 1) {//只有一个节点
first = null;//将first指向null,就是删除
}else {
Node<E> last = node(size - 1);//获取尾节点
first = first.next;//因为index==0,first指向头节点的下一个节点
last.next = first;//尾加点指向first
}
}else {
Node<E> prev = node(index - 1);
node = prev.next;
prev.next = node.next;
}
size--;
return node.element;
}
- 删除元素,要保证尾节点指向头节点,要特殊处理size==1时,first指向null
- 在index!=0时,与双向链表的删除一至
2,双向循环链表
- 双向循环链表就是 头节点的prev指向尾节点,尾节点的next指向头节点
- 当只有一个节点时,first和last都指向同一个节点,prev和next也指向同一个节点
2.1,插入元素
public void add(int index, E element) {
rangeCheckForAdd(index);
// 如果 index == size, 说明添加的索引是最后位置
if (index == size) {
// 创建新节点, prev指向原链表的尾节点, next指向首节点
Node<E> node = new Node<>(last, element, first);
// 当原链表没有任何节点
if (size == 0) {
first = node;
last = node;
node.prev = node;
node.next = node;
}else {
// 原链表尾节点next指向node
last.next = node;
// 原链表头结点prev指向node
first.prev = node;
// last指向新的尾节点
last = node;
}
}else {
// 添加新节点后的下一个节点
Node<E> next = node(index);
// 添加新节点后的上一个节点
Node<E> prev = next.prev;
// 创建新节点, 新节点的上一个节点时prev, 新节点的下一个节点是next
Node<E> node = new Node<>(prev, element, next);
// next的上一个节点是新节点
next.prev = node;
// prev的下一个节点是新节点
prev.next = node;
// 当next == first时, 说明新添加节点的索引是0
if (next == first) {
first = node;
}
}
size++;
}
- 当index==size时,在索引的最后的位置添加节点。index==0只有一个节点时,first,last,node.prev ,node.next都指向同一个节点。index!=0时,需要 尾节点的next指向头节点, 头节点的prev指向尾节点。
2.1,删除元素
public E remove(int index) {
// 需要删除的节点
Node<E> node = node(index);
if (size == 1) {
first = null;
last = null;
}else {
// 删除节点的前一个节点
Node<E> prev = node.prev;
// 删除节点的后一个节点
Node<E> next = node.next;
next.prev = prev;
prev.next = next;
// 如果node == first, 说明删除的是第一个节点
if (node == first) {
first = next;
}
// 如果next == last, 说明删除的是最后一个节点
if (next == last) {
last = prev;
}
}
size--;
return node.element;
}
- 当size==1,只有一个节点时,只需要first和last都指向null
- 当size!=1时,需要处理被删除节点的前一个节点和后一个的prev和next的指向
- 当删除头节点时,需要把first指向头节点的后面一个节点
- 当删除尾节点时,需要把last指向尾节点的前面一个节点
3,约瑟夫问题
问题描述:一个圆环有8个元素,从第一个元素开始,依次删除数到的第三个元素。所以依次删除的顺序为:3,6,1,5,2,8,4,最后只剩下元素7
3.1,如何发挥循环链表的最大威力?
-
可以考虑增设1个成员变量,3个方法
-
current:用于指向某个节点
-
void reset() :让current指向头节点first
-
E next() :让current往后走一步,即current = current.next
-
E remove() : 删除current指向的节点,删除成功后让current指向下一个节点
public void reset() { current = first; } public E next() { if (current == null) return null; current = current.next; return current.element; } public E remove() { if (current == null) return null; Node<E> next = current.next; E element = remove(current); if (size == 0) { current = null;//size==0,current指向null } else { current = next;//current指向下一个节点 } return element; }
3.2,约瑟夫问题解答
使用双向循环链表进行解答,我们已经添加了三个方法使用
static void josephus() {
CircleLinkedList<Integer> list = new CircleLinkedList<>();
for (int i = 1; i <= 8; i++) {
list.add(i);//添加8个元素
}
// 指向头结点(指向1)
list.reset();
while (!list.isEmpty()) {
list.next();//指向第二个节点
list.next();//指向第三个节点
list.remove()
}
}
- 由于时双向循环链表,删除第三个节点,只需要当前节点向后指两次即可,所以list调用next()方法两次