链表一共有四种,单向链表,单向循环链表,双向链表,双向循环列表。单向链表在上篇文章中有过介绍,而单向循环链表和单向链表结构几乎相同,只是循环链表并不像是链,而是一个环,首尾相连。双向链表和双向循环链表的区别也是链和环的区别。双向链表顾名思义,有两个方向。可以从头到尾进行检索,也可以从尾到头反向检索。Java中的LinkedList就是一个双向链表。下面我们将手写一个LinkedList,实现双向链表的增删改查。
一、准备工作
我们创建一个LinkedList类,上篇文章说过,链表是由一个个的节点组成的。所以在LinkedList类中创建一个Node节点类。
注意:上篇文章说过,单链表的节点由两部分组成,而双向链表由三部分组成。pre指针域,数据域,next指针域。pre指针域中存储了上一个节点的内存地址,而next指针域中存储了下一个节点的内存地址。所以Node节点类中三个变量分别表示上述的三个含义。而链中需要保存链的首节点和尾节点。
public class LinkedList<E>{
Node<E>first;//头结点
Node<E>last;//尾节点
int size = 0;//大小
public LinkedList() {
}
private static class Node<E>{
E item;
Node<E>pre;//前一个节点
Node<E>next;//后一个节点
public Node(Node<E>pre,E item,Node<E>next) {
this.pre = pre;
this.item = item;
this.next = next;
}
}
}
二、数据的查询
1.index<(size>>1):判断index是在链的前半部分还是后半部分
2.node=node.next:这个主要是移动node的指针位置,当符合条件的时候,就会返回当前node节点对象,node.item就是存储的数据。
/**
* 获取指定位置的节点
* @param index
* @return
*/
private Node<E> node(int index) {
if(index<(size>>1)) {
Node<E>node=first;
for(int i=0;i<index;i++) {
node = node.next;
}
return node;
}else {
Node<E>node = last;
for(int i=size-1;i>index;i--) {
node = node.pre;
}
return node;
}
}
三、数据的增加
数据增加的核心方法是add双参方法和linklast,具体看一下注释即可。其实核心的操作就是断开连接,然后再将新数据重新连接。写的时候注意一些细节和考虑特殊情况就可以了。
/**
* 如果没有填写index,默认从链的尾部添加
* @param e
*/
public void add(E e) {
add(size,e);
}
/**
* 在指定index的位置添加节点
* @param index
* @param e
*/
public void add(int index ,E e) {
if(index<0||index>size) {
return;
}
if(index==size) {
linkLast(e);
}else {
Node<E>target=node(index);
Node<E>preNode = target.pre;
Node<E>newNode = new Node<E>(preNode,e,target);
if(preNode==null) {
first = newNode;
}else {
preNode.next = newNode;
}
target.pre = newNode;
size++;
}
}
/**
* 将数据添加到链的尾部
* @param e
*/
private void linkLast(E e) {
Node<E>newNode = new Node<E>(last,e,null);
Node<E>lastNode = last;
last = newNode;
if(lastNode==null) {
first = newNode;
}else {
lastNode.next = newNode;
}
size++;
}
四、数据的删除
数据的删除虽然和增加的方法不一样,但是原理其实是一样的,主要就是断开连接,然后再重新连接到新的节点。
/**
* 移除指定位置的节点
* @param index
*/
public void remove(int index) {
Node<E>target = node(index);
unlinkNode(target);
}
/**
* 断开连接
* @param target
*/
private void unlinkNode(Node<E>target) {
Node<E>preNode = target.pre;
Node<E>nextNode = target.next;
if(preNode==null) {
first = nextNode;
}else {
preNode.next = nextNode;
}
if(nextNode==null) {
last = preNode;
}else {
nextNode.pre = preNode;
}
size--;
}
以上实现的就是双向链表的增删改查操作,相对来说还是比较简单。没有什么特殊的骚操作,明白原理后,注意一些细节的地方即可。
欢迎提问,欢迎纠错