双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接前驱和直接后继。所以,从双向链表中的任意结点开始,都可以很方便直接用地址访问它的前驱结点和后继结点。
这里我们将双向循环链表定义为LinkedList类实现List、Deque、Stack接口
这里我们主要讲几个方面的原理,
一、添加结点
如果是直接添加元素结点对象的话直接添加即可
public void add(E element) {
add(size,element);
}
如果是通过index来添加的话就要考虑四种情况:
首先要判断链表是否是否存在索引越界的情况,如果越界就抛一个越界的异常
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException("index outof range");
}
第一种情况
当链表为空的时候
如果为空时,head头指针指向添加的结点,tail尾指针指向新添加的结点,
因为是双向循环链表所以讲tail的后继next指向head,head的前驱pre指向tail
代码如下
if (isEmpty()) {
head = node;
tail = node;
tail.next = head;
head.pre = tail;
}
第二种情况
从链表的头结点处添加
将添加的结点node.pre指向head.pre
node.next指向head
head.pre指向node
将node定义为新的头结点
再将tail.next指向head
添加成功后的链表如图:
代码如下:
else if (index == 0) {
node.pre = head.pre;
node.next = head;
head.pre = node;
head = node;
tail.next = head;
第三种情况
从链表的尾结点处添加
将node.next指向tail.next
tail.next指向node
node.pre指向tail
将node定义为新的tail尾结点
最后将head.pre指向tail
尾处添加成功后的链表图如下:
添加前:
添加后:
代码如下:
else if (index == size) {
node.next = tail.next;
tail.next = node;
node.pre = tail;
tail = node;
head.pre = tail;
第四中情况
从链表的中间处添加
首先的话我们需要定义两个两个指针,p结点为添加的结点的前驱,q结点为结点的next下一跳,也作为添加结点的下一跳
首先的还要考虑添加的结点的添加的位置,如果插入结点在size/2的位置得话,就默认在左边,从head开始向后指向
定义两个结点p,q
p指向p.next
q指向p.next
p.next指向node
node.pre指向p
node.next指向q
最后q.pre指向node
添加结点完成如下图:
代码如下:
Node p,q;
if (index <= size / 2) {
p = head;
for (int i = 0; i < index - 1; i++) {
p = p.next;
}
q = p.next;
p.next = node;
node.pre = p;
node.next = q;
q.pre = node;
然后就是添加的位置靠近尾结点
首先将p指向tai尾结点
然后p=p.pre
q指向p.pre
q.next指向node
p.pre指向node
最后 node.next指向p
添加结点添加之后如图:
代码如下:
else {
p = tail;
for (int i = size - 1; i > index; i--) {
p = p.pre;
}
q = p.pre;
q.next = node;
node.pre = q;
p.pre = node;
node.next = p;
}
无论什么情乱下添加结点;添加结束后都要对链表的长度进行size++
二、删除结点
首先的话就是直接删除元素
通过indexOf方法查找需要删除元素的位置
只要位置不为-1那么就直接删除
public void remove(E element) {
int index = indexOf(element);
if (index != -1) {
remove(index);
}
}
如果是通过index删除的话
跟添加一样首先判断index是否越界然后要考虑删除的位置
大情况分为4种:
只有一个结点
首先定义一个ret用于存放删除的结点
直接将head置null
tail置空
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException("index outof range");
}
E ret = null;
if (size == 1) {
ret = head.data;
head = null;
tail = null;
}
从头结点删除
首先将node指向head.next;
将ret=head.data
head.next置空
node.pre指向head.pre
head.pre置空
head指向node
tail.next指向head;
如下图:
定义一个node结点
断开头节点的所有联系
完全删除后就为这样
代码如下:
else if (index == 0) {
Node node = head.next;
ret = head.data;
head.next = null;
node.pre = head.pre;
head.pre = null;
head = node;
tail.next = head;
}
从尾结点删除
node指向tail.pre
ret=tail.data
tail.pre置null
node.next指向tail.next
tail.next置空
tail指向node
最后head.pre指向tail
如下图:
删除前:
断开所有联系:
删除后:
代码如下:
else if (index == size - 1) {
Node node = tail.pre;
ret = tail.data;
tail.pre = null;
node.next = tail.next;
tail.next = null;
tail = node;
head.pre = tail;
}
从中间删除
从中间删除的话跟添加一样得考虑删除得结点得大致位置
定义三个结点p,q,r
p作为要删除结点得前一个结点
q作为要删除的结点
r作为要删除的后一个结点
要删除的节点在左边时
p指向head
然后移动到p.next
然后将q指向p.next
将q.data的值给ret
r指向q.next
p.next指向r
r.pre指向p
q.next置空
q.pre置空
如下图:
删除后:
代码如下:
else {
Node p,q,r;
if (index <= size / 2) {
p = head;
for (int i = 0; i < index - 1; i++) {
p = p.next;
}
q = p.next;
ret = q.data;
r = q.next;
p.next = r;
r.pre = p;
q.next = null;
q.pre = null;
}
然后的话就是要删除的结点靠近tail尾结点
p从tail开始
p向左移p=p.next
q指向p.pre
将q.data给ret
r指向q.pre
r.next指向p
p.pre指向r
q.next置空
q.pre置空
跟添加一样无论那种情况,删除的话链表的整体长度-1即size–最后返回ret
如图:
删除后:
代码如下:
else {
p = tail;
for (int i = size - 1; i > index; i--) {
p = p.pre;
}
q = p.pre;
ret = q.data;
r = q.pre;
r.next = p;
p.pre = r;
q.next = null;
q.pre = null;
}
}
size--;
return ret;
}
整个代码如下:
public class LinkedList<E> implements List<E>, Stack<E>, Queue<E>, Deque<E> {
private class Node {
E data;
Node pre;
Node next;
public Node(E data) {
this.data = data;
pre = null;
next = null;
}
@Override
public String toString() {
return data.toString();
}
}
private Node head;
private Node tail;
private int size;
public LinkedList() {
head = null;
tail = null;
size = 0;
}
@Override
public void add(E element) {
add(size,element);
}
@Override
public void add(int index, E element) {
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException("index outof range");
}
Node node = new Node(element);
if (isEmpty()) {
head = node;
tail = node;
tail.next = head;
head.pre = tail;
} else if (index == 0) {
node.pre = head.pre;
node.next = head;
head.pre = node;
head = node;
tail.next = head;
} else if (index == size) {
node.next = tail.next;
tail.next = node;
node.pre = tail;
tail = node;
head.pre = tail;
} else {
Node p,q;
if (index <= size / 2) {
p = head;
for (int i = 0; i < index - 1; i++) {
p = p.next;
}
q = p.next;
p.next = node;
node.pre = p;
node.next = q;
q.pre = node;
} else {
p = tail;
for (int i = size - 1; i > index; i--) {
p = p.pre;
}
q = p.pre;
q.next = node;
node.pre = q;
p.pre = node;
node.next = p;
}
}
size++;
}
@Override
public void remove(E element) {
int index = indexOf(element);
if (index != -1) {
remove(index);
}
}
@Override
public E remove(int index) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException("index outof range");
}
E ret = null;
if (size == 1) {
ret = head.data;
head = null;
tail = null;
} else if (index == 0) {
Node node = head.next;
ret = head.data;
head.next = null;
node.pre = head.pre;
head.pre = null;
head = node;
tail.next = head;
} else if (index == size - 1) {
Node node = tail.pre;
ret = tail.data;
tail.pre = null;
node.next = tail.next;
tail.next = null;
tail = node;
head.pre = tail;
} else {
Node p,q,r;
if (index <= size / 2) {
p = head;
for (int i = 0; i < index - 1; i++) {
p = p.next;
}
q = p.next;
ret = q.data;
r = q.next;
p.next = r;
r.pre = p;
q.next = null;
q.pre = null;
} else {
p = tail;
for (int i = size - 1; i > index; i--) {
p = p.pre;
}
q = p.pre;
ret = q.data;
r = q.pre;
r.next = p;
p.pre = r;
q.next = null;
q.pre = null;
}
}
size--;
return ret;
}
@Override
public E get(int index) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException();
}
if (index == 0) {
return head.data;
} else if (index == size - 1) {
return tail.data;
} else {
Node p = head;
for (int i = 0; i < index; i++) {
p = p.next;
}
return p.data;
}
}
@Override
public E set(int index, E element) {
if (index < 0 || index >= size) {
throw new IllegalArgumentException("index outof range");
}
E ret = null;
if (index == 0) {
ret = head.data;
head.data = element;
} else if (index == size - 1) {
ret = tail.data;
tail.data = element;
} else {
Node p = head;
for (int i = 0; i < index; i++) {
p = p.next;
}
ret = p.data;
p.data = element;
}
return ret;
}
@Override
public void addFirst(E element) {
add(0,element);
}
@Override
public void addLast(E element) {
add(size,element);
}
@Override
public E removeFirst() {
return remove(0);
}
@Override
public E removeLast() {
return remove(size - 1);
}
@Override
public E getFirst() {
return get(0);
}
@Override
public E getLast() {
return get(size - 1);
}
@Override
public void offer(E element) {
add(size,element);
}
@Override
public E poll() {
return remove(0);
}
@Override
public E element() {
return get(0);
}
@Override
public int size() {
return size;
}
@Override
public int indexOf(E element) {
int index = 0;
Node p = head;
while (!p.data.equals(element)) {
index++;
p = p.next;
if (p == head) {
return -1;
}
}
return index;
}
@Override
public boolean contains(E element) {
return indexOf(element) != -1;
}
@Override
public boolean isEmpty() {
return size == 0 && head == null && tail == null;
}
@Override
public void push(E element) {
add(size,element);
}
@Override
public E pop() {
return remove(size - 1);
}
@Override
public E peek() {
return get(size - 1);
}
@Override
public void clear() {
head = null;
tail = null;
size = 0;
}
@Override
public void sort(Comparator<E> comparator) {
if (comparator == null) {
throw new IllegalArgumentException("comparator can not be null");
}
if (size < 2) {
return;
}
Node i = head.next;
Node j = null;
Node k = null;
E e = null;
while (i != head) {
e = i.data;
j = i;
k = j.pre;
while (k != tail && comparator.compare(k.data, e) > 0) {
j.data = k.data;
j = j.pre;
k = j.pre;
}
j.data = e;
i = i.next;
}
}
@Override
public List<E> sublist(int fromIndex, int toIndex) {
if (fromIndex < 0 || toIndex >= size || fromIndex >= toIndex) {
throw new IllegalArgumentException("sublist index outof range");
}
Node nodeA = head;
for (int i = 0; i < fromIndex; i++) {
nodeA = nodeA.next;
}
Node nodeB = head;
for (int i = 0; i < toIndex - 1; i++) {
nodeB = nodeB.next;
}
Node p = nodeA;
LinkedList<E> list = new LinkedList<>();
while (true) {
list.add(p.data);
if (p == nodeB) {
break;
}
p = p.next;
}
return list;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append('[');
if (isEmpty()) {
sb.append(']');
} else {
Node p = head;
while (true) {
sb.append(p.data);
if (p == tail) {
sb.append(']');
break;
}
sb.append(',');
p = p.next;
}
}
return sb.toString();
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj == this) {
return true;
}
if (obj instanceof LinkedList) {
LinkedList other = (LinkedList) obj;
if (this.size == other.size) {
if (this.size == 0) {
return true;
}
Node p1 = this.head;
Node p2 = other.head;
while (true) {
if (!p1.data.equals(p2.data)) {
return false;
}
if (p1 == this.tail) {
return true;
}
p1 = p1.next;
p2 = p2.next;
}
} else {
return false;
}
} else {
return false;
}
}
@Override
public Iterator<E> iterator() {
return new LinkedListIterator();
}
private class LinkedListIterator implements Iterator<E> {
private Node cur = head;
private boolean flag = true;
@Override
public boolean hasNext() {
if (isEmpty()) {
return false;
}
return flag;
}
@Override
public E next() {
E ret = cur.data;
cur = cur.next;
if (cur == head) {
flag = false;
}
return ret;
}
}