目录
双向链表的实现(有哨兵)
1、双向链表的定义表示
根据双向链表的形式,定义双向链表的结构如下。
在双向链表中的每一个节点 Node 包含三部分,上一个节点,当前数据,下一个节点地址。
因为存在哨兵,这里定义了两个哨兵,head 和 tail ,没有具体含义,只起到引导作用。
/**
* 双向链表
* 有哨兵节点,哨兵节点是 head 节点,head 节点后面的数据才是真正的数据
*
* @author zjj_admin
*/
public class DoublyLinkedListSentinel implements Iterable<Integer> {
/**
* 头哨兵
*/
private Node head;
/**
* 尾哨兵
*/
private Node tail;
public DoublyLinkedListSentinel() {
//初始化头哨兵和尾哨兵
this.head = new Node(null, Integer.MIN_VALUE, null);
this.tail = new Node(null, Integer.MAX_VALUE, null);
// 让头哨兵和尾哨兵相连
head.next = tail;
tail.prev = head;
}
/**
* 链表中的节点,链表和节点是组合的关系,所以做成内部内比较合适。
*/
private static class Node {
/**
* 上一个节点
*/
Node prev;
/**
* 节点数据
*/
int value;
/**
* 当前节点的下一个指针
*/
Node next;
public Node(Node prev, int value, Node next) {
this.prev = prev;
this.value = value;
this.next = next;
}
}
}
2、头部添加数据
/**
* 在链表的头部插入一个数据
*
* @param value 待插入数据
*/
public void addFirst(int value) {
Node next = head.next;
Node inserted = new Node(head, value, next);
//让上一个节点的 next 指针指向带插入节点
head.next = inserted;
//让下一个极点的 prev 指针指向待插入节点
next.prev = inserted;
}
3、尾部添加数据
/**
* 添加数据到尾结点
*
* @param value
*/
public void addLast(int value) {
Node last = findLast();
if (last == null) {
//没有数据
addFirst(value);
return;
}
//最后节点的下一个节点,一般是 tail 节点
Node next = last.next;
Node inserted = new Node(last, value, next);
last.next = inserted;
//让下一个极点的 prev 指针指向待插入节点
next.prev = inserted;
}
/**
* 找到当前链表的最后一个有效节点
*
* @return
*/
private Node findLast() {
if (head.next == tail) {
return null;
}
Node p = head.next;
while (p != null) {
if (p.next == tail) {
return p;
}
p = p.next;
}
return null;
}
4、获取链表数据和长度
/**
* 获取链表的有效长度
*
* @return
*/
public int size() {
Node p = head.next;
int size = 0;
while (p != tail) {
size++;
p = p.next;
}
return size;
}
/**
* 获取链表中的所有数据
*
* @return 数据数组
*/
public int[] values() {
int size = size();
int[] res = new int[size];
Node p = head.next;
int i = 0;
while (p != tail) {
res[i] = p.value;
p = p.next;
i++;
}
return res;
}
5、根据索引获取对应节点的数据
/**
* 获取索引为 index 对应节点的数据
*
* @param index
* @return
*/
public int get(int index) {
Node node = getNode(index);
if (node == head || node == tail || node == null) {
throw new IndexOutOfBoundsException("index:" + index + " 越界了");
}
return node.value;
}
/**
* 获取索引为 index 对应节点
* 头哨兵的索引为 -1
*
* @param index
* @return
*/
private Node getNode(int index) {
int i = -1;
Node p = head;
while (p != null) {
if (i == index) {
return p;
}
i++;
p = p.next;
}
return null;
}
6、在指定索引的位置添加数据
/**
* 在指定的索引位置添加数据
*
* @param index
* @param value
*/
public void add(int index, int value) {
//获取上一个节点
Node prev = getNode(index - 1);
if (prev == null) {
throw new IndexOutOfBoundsException("index:" + index + " 越界了");
}
Node next = prev.next;
//需要插入的节点
Node inserted = new Node(prev, value, next);
//让上一个节点的 next 指针指向带插入节点
prev.next = inserted;
//让下一个极点的 prev 指针指向待插入节点
next.prev = inserted;
}
7、移除链表中的元素
/**
* 移除链表中的第一个元素
*
* @return
*/
public int removeFirst() {
Node p = head.next;
if (p == tail) {
throw new NullPointerException("链表没有数据了");
}
Node next = p.next;
head.next = next;
next.prev = head;
return p.value;
}
/**
* 根据索引数据
*
* @param index 删除索引
* @return 被删除的索引节点数据
*/
public int remove(int index) {
//获取待删除的节点
Node removed = getNode(index);
if (removed == null || removed == head || removed == tail) {
throw new IndexOutOfBoundsException("index:" + index + " 越界了");
}
Node prev = removed.prev;
Node next = removed.next;
prev.next = next;
next.prev = prev;
return removed.value;
}
8、遍历链表
/**
* 遍历链表,使用函数式接口 Consumer
*/
public void loop(Consumer<Integer> consumer) {
Node p = head.next;
//当前节点不为 null 就一直循环
while (p != tail) {
// accept 里面传入相关的参数
consumer.accept(p.value);
p = p.next;
}
}
9、基于增强 for 进行遍历
需要实现 Iterable 接口
/**
* 双向链表
* 有哨兵节点,哨兵节点是 head 节点,head 节点后面的数据才是真正的数据
*
* @author zjj_admin
*/
public class DoublyLinkedListSentinel implements Iterable<Integer> {
/**
* 基于迭代器进行循环
*
* @return
*/
@Override
public Iterator<Integer> iterator() {
return new Iterator<Integer>() {
Node p = head.next;
@Override
public boolean hasNext() {
return p != tail;
}
@Override
public Integer next() {
int v = p.value;
p = p.next;
return v;
}
};
}
}