双向链表
什么是双向链表: 在之前我们提到过,每一个节点只有一个指针域的叫做单链表,所以在这里就是每一个节点有两个指针域的叫做双链表;一个指向直接前驱,一个指向直接后继;
为什么需要双向链表: 因为在单链表中,查找直接后继的时间复杂度为O(1),而查找直接前驱的时间复杂度为O(n),为了克服这一缺点,就诞生了 双向链表(Double Linked List) ;
ps:如果有不正确之处,望各位看官不吝赐教,毕竟我还是菜鸟
双向链表的实现
链表的初始化:
/**
* 首元素
*/
private Node first;
/**
* 尾元素
*/
private Node last;
/**
* 临时节点,用于遍历链表
*/
private Node temp;
/**
* 双链表中的元素个数
*/
private int size;
添加元素操作:
-
末尾处加入节点:
/** * 在链表的末尾处加入元素 * @param element 元素值 */ public void add(E element) { // 创建新的节点 Node<E> node = new Node<>(element); // 先判断是否是空链表,直接加入 if (size == 0) { first = node; last = node; } else { last.next = node; node.prior = last; last = node; } size ++; }
-
插入节点:
/** * 在指定的位置处插入给定的数据元素 * @param index 指定的位置 * @param element 数据元素 */ public void insert(int index, E element) { // 检查索引 checkIndex(index); // 创建新的节点 Node<E> node = new Node<>(element); temp = first; // 临时节点 if (temp != null) { if (index == 0) { // 插入的位置是首节点 node.next = temp; temp.prior = node; first = node; } else if (index == size) { // 插入的位置是尾节点 add(element); } else { // 获取指定位置处的节点 temp = getNode(index); // 插入数据 temp.next.prior = node; node.next = temp.next; temp.next = node; node.prior = temp; } } size ++; }
获取操作:
-
根据索引获取元素:
/** * 根据给定的索引位置返回节点 * @param index 指定的索引位置 * @return */ public Node getNode(int index) { checkIndex(index); if (index < (size >> 1)) { // 在左半部分,从first开始往后遍历 temp = first; for (int i = 0; i < index; i++) { temp = temp.next; } } else { // 右半部分,从last开始往前遍历 temp = last; for (int i = size - 1; i > index; i--) { temp = temp.prior; } } return temp; }
-
根据元素获取索引:
/** * 找到所给元素的索引 * @param element 需要匹配的元素 * @return 返回匹配到的索引值 */ public int getIndex(E element) { temp = first; for (int i = 0; i < size; i++) { if (temp.element.equals(element)) return i; temp = temp.next; } return -1; }
删除操作:
-
根据索引删除节点:
/** * 删除指定位置处的节点 * @param index 需要删除的节点位置 */ public void delete(int index) { checkIndex(index); if (index == 0) { first = first.next; first.next.prior = null; } else if (index == size -1) { last.prior.next = null; last = last.prior; } else { // 获取需要删除的节点 temp = getNode(index); temp.prior.next = temp.next; temp.next.prior = temp.prior; } size --; }
-
根据元素值删除节点:
/** * 删除指定的元素 * @param element 需要删除的元素 */ public void remove(E element) { // 获取该元素的索引 int index = getIndex(element); // 删除元素 delete(index); }
修改操作:
/**
* 修改指定位置的元素
* @param index 需要修改的位置
* @param element 需要替换的元素
*/
public void set(int index, E element) {
checkIndex(index);
// 根据index找到节点
temp = getNode(index);
// 修改元素
temp.element = element;
}
辅助方法:
/**
* 检查所给的索引位置是否合法
* @param index 链表中的位置
*/
private void checkIndex(int index) {
if (index < 0 || index >= size) {
throw new RuntimeException("索引不合法:" + index);
}
}
@Override
public String toString() {
Node temp = first;
StringBuilder sb = new StringBuilder("[");
if (temp != null) {
for (int i = 0; i < size; i++) {
sb.append(temp.element).append(",");
temp = temp.next;
}
}
sb.setCharAt(sb.length() - 1, ']');
return sb.toString();
}
节点类:
/**
* 节点类
* @param <E>
*/
public static class Node<E>{
/**
* 前指针域
*/
Node prior;
/**
* 数据域
*/
E element;
/**
* 后指针域
*/
Node next;
public Node() {}
public Node(E element) {
this.prior = null;
this.element = element;
this.next = null;
}
}