双向循环链表中,每个节点都有2个指针,分别指向前一个节点和后一个节点。
链表头有一个head,是空节点,head节点的前一个节点指向链表最后一个节点,链表最后一个节点的后一个节点是head节点。
查询的时候,如果index小于链表长度一半,从头开始查询,如果index长度大于链表长度一半,则从末尾开始查询。故,时间复杂度是O(n/2)
内存空间不一定是连续的,由于查询需要遍历每一个节点,并取获节点的next节点。遍历查询效率不如数组。但是中间的插入,删除的效率优于数组。
在java中,linkedList就是依赖双链表实现的。
下面是手撸实现的记录:
public class DoubleLink<T> {
private DNode<T> mHead; //头节点
private int mCount; //节点个数
public DoubleLink() {
mHead = new DNode<T>(null, null, null);
mHead.pre = mHead.next = mHead;
mCount = 0;
}
public int size() {
return mCount;
}
private DNode<T> getNode(int index) {
if (index < 0 || index >= mCount) {
throw new IndexOutOfBoundsException();
}
//正向查找
if (index < mCount / 2) {
DNode<T> node = mHead.next;
for (int i = 0; i < index; i++) {
node = node.next;
}
return node;
}
//反向查找
DNode<T> node = mHead.pre;
//画图就能看出来,反过来算index = 末尾指针(mCount-1) - 目标index(index)
int rIndex = mCount - 1 - index;
for (int i = 0; i < rIndex; i++) {
node = node.pre;
}
return node;
}
//获取第i个位置的节点的值
public T get(int index) {
return getNode(index).value;
}
public T getLast() {
return getNode(mCount - 1).value;
}
//将节点插入到index位置之前
public void add(int index, T t) {
if (index < 0 || index > mCount) {
throw new IndexOutOfBoundsException();
}
if (index == 0) {
//根据参数t,和位置0,创建了一个我们需要插入的值
DNode<T> node = new DNode<>(mHead, mHead.next, t);
//将原本head的下一个节点的向上的指针指向我们的目标node
mHead.next.pre = node;
//头节点的下一个指针,指向我们的node
mHead.next = node;
} else {
DNode<T> nextNode = getNode(index);
//创建我们的目标node
DNode<T> node = new DNode<>(nextNode.pre, nextNode, t);
nextNode.pre.next = node;
nextNode.pre = node;
}
//不要忘记了!
mCount++;
}
public void addFirst(T t) {
add(0, t);
}
//末尾插入
public void add(T t) {
if (mCount == 0) {
addFirst(t);
} else {
DNode<T> node = new DNode<>(mHead.pre, mHead, t);
mHead.pre.next = node;
mHead.pre = node;
mCount++;
}
}
//删除节点
public void delete(int index) {
DNode<T> node = getNode(index);
DNode<T> nodePre = node.pre;
node.next.pre = nodePre.pre;
mCount--;
}
//删除第一个个节点
public void deleteFirst() {
delete(0);
}
//删除最后一个节点
public void deleteLast() {
delete(mCount - 1);
}
private static class DNode<T> {
DNode<T> pre;
DNode<T> next;
T value;
public DNode(DNode pre, DNode next, T value) {
this.pre = pre;
this.next = next;
this.value = value;
}
}
}