自己动手写一个简单的双向链表结构
在前面两篇博客中,学习了单向链表和双端链表,并尝试着自己动手去实现了这两个结构,如果你看过那两篇文章中的代码,你可能会发现那两篇代码的实现均没有使用泛型,只是做了一个逻辑功能的实现,可能没有什么实际的应用价值。
这篇文章,主要是沉淀自己对双向链表结构的学习,在学习过单向链表和双端链表之后,再学习双向链表,还是比较容易理解的,就逻辑来讲,无非是加了一个向前引用嘛。所以在这篇文章中,我尝试去写一个双向链表结构,并且使用了泛型,这样数据结点的数据类型就不会像前几篇那样局限于单一的数据类型了。
数据结点结构代码:
package mydoublylinkedlist;
public class Node<T> {
// 创建结点,使用泛型
public T data;
// 创建结点的下一个引用
public Node<T> next;
// 创建结点的上一个引用
public Node<T> previous;
/**
* 构造方法,创建包含数据的结点
* @param value
*/
public Node(T value) {
this.data = value;
}
/**
* 显示当前结点的数据到控制台
*/
public void show() {
System.out.print(data + " ");
}
}
链表结构主体代码:
package mydoublylinkedlist;
public class LinkList<T> {
// 定义链表的头结点
private Node<T> first;
// 定义链表的尾结点
private Node<T> last;
/**
* 构造方法,头结点尾结点默认为null
*/
public LinkList() {
first = null;
last = null;
}
/**
* 头插法
*
* @param value
*/
public void insertFirst(T value) {
// 创建新的结点
Node<T> node = new Node<T>(value);
// 如果头结点是空,即链表是空的
if (first == null) {
last = node;
} else {
// 如果已经有结点了
node.next = first;
first.previous = node;
}
// 将新的结点引用赋给头结点
first = node;
}
/**
* 尾插法
*
* @param value
*/
public void insertLast(T value) {
// 新建的结点
Node<T> node = new Node<T>(value);
// 如果是个空链表
if (first == null) {
first = node;
} else {
// 如果不是空链表
node.previous = last;
last.next = node;
}
// 将新建结点引用指向尾结点
last = node;
}
/**
* 从头部删除
*/
public void delFirst() {
// 如果头结点不为空
if (first != null) {
// 如果头结点的下一个结点为空,则表明就剩它一个结点了
if (first.next == null) {
// 直接将头结点设为null
first = null;
last = null;
} else {
// 如果头结点的下一个结点还有数据,将头结点的下一个结点引用定义为头结点
first = first.next;
// 因为是双向链表,所以将当前头结点的上一个引用也要赋值为null才能彻底删除
first.previous = null;
}
} else {
// 如果头结点为空,则表明链表中已经没有数据结点了
System.out.println("已经没有东西可删啦,快住手!");
}
}
/**
* 从尾部删除
*/
public void delLast() {
// 判断尾结点不为空
if(last != null) {
// 判断如果尾结点的前一个结点为null,则表明就剩下一个结点了
if(last.previous == null) {
first = null;
last = null;
}else {
// 如果剩下的数据结点大于两个,则双向删除
last = last.previous;
last.next = null;
}
}else {
System.out.println("已经没有数据结点可删啦,快住手!");
}
}
/**
* 显示链表中所有的数据,打印到控制台
*/
public void showall() {
// 遍历打印
Node<T> currentNode = first;
while (currentNode != null) {
currentNode.show();
currentNode = currentNode.next;
}
}
/**
* 查找数据结点并返回匹配到的结点
* 跟单向链表的查询一样,注释我就不写了,在前几篇博客中的代码中有
* @param value
* @return
*/
public Node<T> search(T value) {
Node<T> currentNode = first;
while (currentNode.data != value) {
if (currentNode.next == null) {
return null;
}
currentNode = currentNode.next;
}
return currentNode;
}
/**
* 删除指定结点
* @param value
* @return
*/
public Node<T> delNode(T value) {
Node<T> currentNode = first;
while (currentNode.data != value) {
if (currentNode.next == null) {
return null;
}
currentNode = currentNode.next;
}
if (currentNode == first) {
this.delFirst();
} else {
currentNode.previous.next = currentNode.next;
}
return currentNode;
}
}
测试代码:
package mydoublylinkedlist;
public class TestLinkList {
public static void main(String[] args) {
LinkList<Integer> list = new LinkList<>();
list.insertLast(52);
list.insertLast(53);
list.insertLast(54);
list.insertFirst(55);
list.insertFirst(56);
list.insertFirst(57);
System.out.println("插入完成后,链表中的数据:");
list.showall();
System.out.println();
System.out.println("头删法删去一个数据后,链表中剩下的数据:");
list.delFirst();
list.showall();
list.delLast();
System.out.println();
System.out.println("尾删法删去一个数据后,链表中剩下的数据:");
list.showall();
System.out.println();
list.delNode(52);
System.out.println("删除指定数字“52“后,链表中剩下的数据:");
list.showall();
}
}
控制台输出结果:
插入完成后,链表中的数据:
57 56 55 52 53 54
头删法删去一个数据后,链表中剩下的数据:
56 55 52 53 54
尾删法删去一个数据后,链表中剩下的数据:
56 55 52 53
删除指定数字“52“后,链表中剩下的数据:
56 55 53