双向链表其实与单链表类似,只是存储结构略有不同。单链表采用指针指向下一个节点的方式,而双向链表多了一个指针指向前一个节点。
单链表的原理与实现参考:
简谈JAVA基础--单链表
双链表与单链表相比多了一个指针变量来指向下一个节点的地址。
每个节点包含三部分: prev前一个节点的指针变量,data节点数据, next 后一个节点的指针变量
在单链表中,由于只有next,所以每个节点的数据地址由上一个节点所知,而对链表的所有操作,都需要通过一个头节点来开始进行。
而双向链表中,由于有了prev的存在,所以可以通过尾节点和头节点来开始进行操作。知道了某个节点,就得到了前后的节点所在位置。
在java中,LinkedList底层就是通过一个双向链表来实现的。有时间可以看一下源码,写的很精妙。
下面是根据LinkedList自己实现的双链表。
结点结构:
链表的存储结构:
插入操作
删除操作
实现双向链表源码( 可以看一下JDK中的LinkedList ):
package Chapter1;
import java.lang.reflect.Array;
import java.util.Collection;
/**
* 实现双向链表。
* Created by yukaiji on 2017/9/13.
*/
public class MyLinkedList<T> implements java.io.Serializable {
private static final long serialVersionUID = -8247167820457785557L;
/** List长度 **/
private int size;
/** 操作次数 **/
private int count;
/** 头节点 **/
private Node<T> head;
/** 尾节点 **/
private Node<T> last;
/**
* 节点类
*/
private class Node<T> {
private T data;
private Node<T> prev;
private Node<T> next;
public Node(Node<T> prev, T data, Node<T> next) {
this.data = data;
this.prev = prev;
this.next = next;
}
}
/**
* 构造方法
*/
public MyLinkedList() {
}
/**
* 添加对象到表头
*/
public void addFirst(T data) {
Node<T> headNode = head;
// 创建一个新的节点
Node<T> newNode = new Node<>(null, data, headNode);
// 将头节点指向新的节点
head = newNode;
// 如果原先的头节点是null 代表新添加的该链表的第一个节点。
// 将尾节点指向新节点,因为该链表只存在一个节点,头尾相同
if (headNode == null) {
last = newNode;
} else {
// 如果不是第一个节点。则将前一个节点的perv指向新添加的节点
headNode.prev = newNode;
}
size++;
count++;
}
/**
* 添加对象到表尾
*/
public void addLast(T data) {
Node<T> lastNode = last;
// 创建一个新的节点
Node<T> newNode = new Node<T>(lastNode, data, null);
// 将尾节点指向新的节点
last = newNode;
// 如果原先的尾节点为空,代表该节点为该链表的第一个节点
// 头节点也为新节点,首尾相同
if (lastNode == null) {
head = newNode;
} else {
// 如果不是第一个节点。则将前一个节点的next指向新添加的节点
lastNode.next = newNode;
}
size++;
count++;
}
/**
* 从链表头部删除一个节点
*/
public void removeFirst() {
// 创建头节点的下一个节点对象
Node<T> headNext = head.next;
// 因为要将该头结点删除,所以把数据和下个节点的指针置为空
head.data = null;
head.next = null;
// 头节点指向该节点的下一个 (也就是删除头结点)
head = headNext;
// 如果头结点的下一个为空,说明要删除的节点是链表中唯一的节点。
// 将尾节点置为null
if (headNext == null) {
last = null;
} else {
// 否则将删除后的头节点的prev置为null (头节点perv永远为null)
head.prev = null;
}
size--;
count++;
}
/**
* 从链表尾部删除一个节点(内容与removeFirst相反,参考removeFirst)
*/
public void removeLast() {
Node<T> lastPrev = last.prev;
last.data = null;
last.prev = null;
last = lastPrev;
if (lastPrev == null) {
head = null;
} else {
last.next = null;
}
size--;
count++;
}
/**
* 打印链表
*/
public void printList() {
for (Node node = head; node != null; node = node.next) {
System.out.print(node.data + " ");
}
System.out.println();
}
/**
* 链表长度
*/
public int size() {
return size;
}
/**
* 链表是否为空
*/
public boolean isEmpty() {
return head == null && last == null;
}
/**
* 链表是否包含对象o
*/
public boolean contains(Object o) {
return indexOf(o) != -1;
}
/**
* 链表转换为数组
*/
public Object[] toArray() {
Object[] array = new Object[size];
int i = 0;
for (Node node = head; node != null; node = node.next) {
array[i++] = node.data;
}
return array;
}
/**
* 链表转换为指定类型数组
*/
public <E> E[] toArray(E[] arr) {
// 如果传递的数组长度小于链表的长度,创建一个指定类型、链表长度的新数组
if (arr.length < size) {
arr = (E[]) Array.newInstance(arr.getClass().getComponentType(), size);
}
Object[] array = arr;
int i = 0;
for (Node node = head; node != null; node = node.next) {
array[i++] = node.data;
}
// 将最后一位置为null代表数组结束
if (arr.length > size) {
array[size] = null;
}
return arr;
}
/**
* 获得传递对象所在的索引位置
*/
public int indexOf(Object o) {
int index = 0;
if (o == null) {
for (Node node = head; node != null; node = node.next) {
if (node.data == null) {
return index;
}
index++;
}
} else {
for (Node node = head; node != null; node = node.next) {
if (o.equals(node.data)) {
return index;
}
index++;
}
}
return -1;
}
/**
* 添加操作,默认采用表尾插入
*/
public void add(T t) {
addLast(t);
}
/**
* 删除指定节点。
* 这里有四种情况:
* 1.节点在头部
* 2.节点在尾部
* 3.节点在中间
* 4.无节点
*/
public T removeNode(Node<T> node) {
T data = node.data;
Node<T> nodeNext = node.next;
Node<T> nodePrev = node.prev;
if (nodePrev == null) {
head = nodeNext;
} else {
nodePrev.next = nodeNext;
}
if (nodeNext == null) {
last = nodePrev;
} else {
nodeNext.prev = nodePrev;
}
size--;
count++;
return data;
}
/**
* 根据传递对象删除第一个遇到的元素。
*/
public boolean remove(Object o) {
if (o == null) {
for (Node<T> node = head; node != null; node = node.next) {
if (node.data == null) {
removeNode(node);
return true;
}
}
} else {
for (Node<T> node = head; node != null; node = node.next) {
if (o.equals(node.data)) {
removeNode(node);
return true;
}
}
}
return false;
}
/**
* 删除c列表中所有在链表中对应的第一个相同元素。
*/
public boolean removeAll(Collection<?> c) {
Object[] objs = c.toArray();
for (Object o : objs) {
T t = (T) o;
remove(t);
}
return true;
}
/**
* 删除index位置的节点
*/
public T remove(int index) {
return removeNode(node(index));
}
/**
* 清空链表
*/
public void clear() {
for (Node<T> node = head; node != null; ) {
Node<T> nodeNext = node.next;
node.data = null;
node.next = null;
node.prev = null;
node = nodeNext;
}
head = last = null;
size = 0;
count++;
}
/**
* 将c插入到链表尾部
*/
public boolean addAll(Collection<? extends T> c) {
// 直接调用在链表尾部插入c
return addAll(size, c);
}
/**
* 将c插入到链表的index节点前
*/
public boolean addAll(int index, Collection<? extends T> c) {
indexIsException(index);
Object[] array = c.toArray();
int num = array.length;
if (num == 0) {
return false;
}
Node<T> pred, succ;
// 这里判断是否从尾部插入
if (index == size) {
succ = null;
pred = last;
} else {
// 获得index位置的节点
succ = node(index);
// 记录该节点的前一个节点。
pred = succ.prev;
}
for (Object o : array) {
// 类型转换
T t = (T) o;
// 创建新的节点,前节点指针指向index的前节点。
Node<T> node = new Node<>(pred, t, null);
// 如果index前节点为null ,代表index 为头节点
if (pred == null) {
// 这样将头结点指向新插入的节点
head = node;
} else {
// 将index前节点的后节点指针指向新的节点(因为插入的新节点在指定index的前面)
pred.next = node;
}
pred = node;
}
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
size += num;
count++;
return true;
}
/**
* 获取index节点数据
*/
public T get(int index) {
return node(index).data;
}
public boolean retainAll(Collection<?> c) {
return false;
}
/**
* 在索引index处添加一个对象
*/
public void add(int index, T element) {
indexIsException(index);
if (index == size) {
addLast(element);
} else {
Node<T> succ = node(index);
Node<T> prev = succ.prev;
Node<T> node = new Node<>(prev, element, succ);
succ.prev = node;
if (prev == null) {
head = node;
} else {
prev.next = node;
}
}
size++;
count++;
}
/**
* 判断索引是否小于0
*/
public Boolean indexIsException(int index) {
if (index < 0) {
System.out.println("索引小于0");
return false;
}
return true;
}
/**
* 返回指定对象最后出现的位置
*/
public int lastIndexOf(Object o) {
int count = size;
if (o == null) {
for (Node<T> node = last; node != null; node = node.prev) {
count--;
if (node.data == null) {
return count;
}
}
} else {
for (Node<T> node = last; node != null; node = node.prev) {
count--;
if (o.equals(node.data)) {
return count;
}
}
}
return -1;
}
/**
* 获取index位置的节点
*/
Node<T> node(int index) {
// 这里为什么判断传过来的index 是否小于链表长度的一半呢?
// 因为这里涉及到效率问题,如果小于一半,从前向后遍历,如果大于一般从后向前遍历。
if (index < (size >> 1)) {
Node<T> x = head;
// 从前向后遍历,x每次为当前节点的下一个,直到找到index节点
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<T> x = last;
// 从后向前遍历,x每次为当前节点的前一个,直到找到index节点
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
}
测试函
public static void main(String[] args) {
MyLinkedList<String> myLinkedList = new MyLinkedList<String>();
myLinkedList.add("a");
myLinkedList.add("b");
myLinkedList.add("c");
myLinkedList.add("d");
myLinkedList.add("e");
myLinkedList.printList();
myLinkedList.remove("c");
myLinkedList.printList();
myLinkedList.addFirst("1");
myLinkedList.printList();
myLinkedList.addLast("2");
myLinkedList.printList();
List<String> addList = new ArrayList<String>();
addList.add("A");
addList.add("B");
myLinkedList.addAll(addList);
myLinkedList.printList();
System.out.println(myLinkedList.get(3));
System.out.println(myLinkedList.contains("d"));
System.out.println(myLinkedList.indexOf("e"));
myLinkedList.removeFirst();
myLinkedList.printList();
myLinkedList.removeLast();
myLinkedList.printList();
myLinkedList.clear();
myLinkedList.printList();
}
、