(一)双向循环链表:简介
(二)双向循环链表:add
(三)双向循环链表:remove
(四)增强型双向循环链表
(五)练习:约瑟夫问题(Josephus Problem)
(六)静态链表
(七)作业题
(八)进一步优化ArrayList
在双向链表的基础上,第一个节点的prev指向最后一个节点,最后一个节点的next指向第一个节点,就是双向循环链表
同样的,我们只需要关注add()
方法和remove()
方法
首先考虑只有1个节点(add后)的情况,代码如下:
然后考虑往尾节点的位置添加节点,代码如下:
整体代码如下:
@Override
public void add(int index, E element) {
rangeCheckForAdd(index);
if (index == size) {//往最后面添加元素
Node<E> oldLast = last;
last = new Node<E>(element, oldLast, first);//新的last节点的next是first
if (oldLast == null) {//这是链表添加的第一个元素
first = last;
first.next = first;
first.prev = first;
} else {
oldLast.next = last;//只需要再接last前一个结点的next就可以了
first.prev = last;
}
} else {//往前面添加元素
Node<E> next = node(index);//如果要往2的位置添加,那2就是新元素的next
Node<E> prev = next.prev;//新节点的上一个元素就是2的上一个(就是1)
Node<E> node = new Node<>(element, prev, next);//已经接好两根线了(新节点的prev和next)
next.prev = node;
prev.next = node;
if (index == 0) {
first = node;
}
}
size++;
}
@Override
public E remove(int index) {
rangeCheck(index);
Node<E> node = first;
if (size == 1) {
first = null;
last = null;
} else {
node = node(index);
Node<E> prev = node.prev;
Node<E> next = node.next;
prev.next = next;
next.prev = prev;
if (index == 0) {
first = next;
}
if (index == size - 1) {
last = prev;
}
}
size--;
return node.element;
}
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;
} else {
current = next;
}
return element;
}
private E remove(Node<E> node) {
if (size == 1) {
first = null;
last = null;
} else {
Node<E> prev = node.prev;
Node<E> next = node.next;
prev.next = next;
next.prev = prev;
if (node == first) { // index == 0
first = next;
}
if (node == last) { // index == size - 1
last = prev;
}
}
size--;
return node.element;
}
之前的remove(int index)
方法也可以被优化,如下:
@Override
public E remove(int index) {
rangeCheck(index);
return remove(node(index));
}
static void josephus() {
CircleLinkedList<Integer> list = new CircleLinkedList<>();
for (int i = 1; i <= 8; i++) {
list.add(i);
}
// 指向头结点(指向1)
list.reset();
while (!list.isEmpty()) {
list.next();
list.next();
System.out.println(list.remove());
}
}
public static void main(String[] args) {
josephus();
}
203. 移除链表元素:https://leetcode-cn.com/problems/remove-linked-list-elements/
83. 删除排序链表中的重复元素:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list/
876. 链表的中间结点:https://leetcode-cn.com/problems/middle-of-the-linked-list/
之前的ArrayList对0号节点进行增删操作都是很慢的,我们可以用一个first变量去优化,如下:
此时删除0号节点,并且更新first的值
再删除一个
此时再往0号节点的位置添加节点
再增加一个
如果此时还要再增加一次,情况如下:
此时可以通过取模运算来获取真实的位置
如:
如果要往红色框框的位置增加节点,之前的做法是挪动后面的节点
现在我们可以挪动比较少的节点的一边(左边的两个),然后更新first
删除节点也类似,这里不再演示