1. 应用场景
- 当不知道数据量大小的时候,可以使用“链表”来动态储存和处理数据
- 需要对链表进行反向操作时
2. 思路
- 分类:(本文使用 “有头节点”)
- 有头节点 链表
- 无头节点 链表
- 存储方式:节点
- 节点结构:
- 数据域(Data):存储 当前节点 的 数据
- 地址域1(Next):指向 下一个节点 的 地址
- 地址域2(Pre):指向 上一个节点 的 地址
- 头指针:数据域无意义,只有指向下一节点的地址域
- 不一定 是连续存储
- 本文章按照节点值的 从小到大 顺序添加
3. 数据结构(类结构):
【节点 DoubleNode】
(1)成员变量(Field)
- data: int,节点所储存的数据
- nextNode: Node,所指向的下一个节点
- preNode: Node,所指向的上一个节点
private int data;
private DoubleNode nextNode;
private DoubleNode preNode;
(2)初始化 / 构造器(Constructor)
- 参数传入节点所要储存的数据
- 初始化节点数据
public DoubleNode(int data) {
this.data = data;
}
(3)方法(Methods)
- 三个成员变量的getter和setter
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public DoubleNode getNextNode() {
return nextNode;
}
public void setNextNode(DoubleNode nextNode) {
this.nextNode = nextNode;
}
public DoubleNode getPreNode() {
return preNode;
}
public void setPreNode(DoubleNode preNode) {
this.preNode = preNode;
}
- toString方法
@Override
public String toString() {
return "DoubleNode{" +
"data=" + data +
'}';
}
【有序双向链表 OrderedDoubleLinkedList】
(1)成员变量(Field)
- headPointer: Node,头节点,不储存数据,只通过自身的nextNode负责储存第一个Node的地址。当有序单链表创建时直接实例化,数据域为0。
private DoubleNode headPointer = new DoubleNode(0);
(2)初始化 / 构造器(Constructor)
- 默认构造器
(3)方法(Methods)
- 添加新节点
addNode(Node) -> void
- 遍历查找插入点
- 将新节点插入到当前节点和下一节点之间
/**
* 添加新节点到双向链表
* @param newNode - 要添加的新节点
*/
public void addNode(DoubleNode newNode) {
// 1. 遍历查找插入点
// 1.1 设置节点变量,用来在遍历时储存当前节点
DoubleNode currentNode = this.headPointer;
// 1.2 遍历双向链表,寻找插入点,找到时结束遍历
while (true) {
// (1) 到达末尾 or 当前节点的next节点值大于新节点值 时,找到插入点(当前值的下一位),结束遍历
if (currentNode.getNextNode() == null || currentNode.getNextNode().getData() > newNode.getData()) {
break;
}
// (2) 找到相同值,抛出异常
else if (currentNode.getNextNode().getData() == newNode.getData()){
throw new RuntimeException("The node is existing!");
}
// (3) 当前节点的next节点值小于新节点值,继续遍历
currentNode = currentNode.getNextNode();
}
// 2. 将新节点插入到当前节点和下一节点之间
// 2.1 设置临时节点变量,用来储存当前节点的下一节点
DoubleNode nextNode = currentNode.getNextNode();
// 2.2 连接当前节点和新节点
currentNode.setNextNode(newNode);
newNode.setPreNode(currentNode);
// 2.3 连接当前节点和next节点
if (nextNode != null) {
newNode.setNextNode(nextNode);
nextNode.setPreNode(newNode);
}
}
- 查找某节点是否存在
isNodeExist(Node) -> boolean
- 若链表为空,抛出异常
- 遍历查找相同值
- 返回boolean变量
/**
* 查找节点是否存在于双向链表中
* @param node - 要查找的节点
* @return boolean - 是否存在
*/
public boolean isNodeExist(DoubleNode node) {
// 1. 若链表为空,抛出异常
if (this.headPointer.getNextNode() == null) {
throw new RuntimeException("The list is empty! ");
}
// 2. 遍历查找相同值
// 2.1 设置boolean变量,用来标识是否找到相同值
boolean found = false;
// 2.2 设置节点变量,用来在遍历时储存当前节点
DoubleNode currentNode = this.headPointer.getNextNode();
// 2.3 循环遍历,查找相同值,找到or到达末尾时,结束遍历
while (currentNode != null) {
// (1) 若当前节点值等于输入节点值,boolean变量设置为true,结束遍历
if (currentNode.getData() == node.getData()) {
found = true;
break;
}
// (2) 否则移动到下一节点,继续循环
currentNode = currentNode.getNextNode();
}
// 3. 返回boolean变量
return found;
}
- 删除某节点
deleteNode(Node) -> void
- 若链表为空,抛出异常
- 遍历查找待删除节点,找到后删除(无需辅助节点,直接删除)
/**
* 删除双向链表中的某节点
* @param node -- 要删除的节点
*/
public void deleteNode(DoubleNode node) {
// 1. 若链表为空,抛出异常
if (this.headPointer.getNextNode() == null) {
throw new RuntimeException("The list is empty");
}
// 2. 遍历查找待删除节点,找到后删除
// 2.1 设置节点变量,用来在遍历时储存当前节点
DoubleNode currentNode = this.headPointer.getNextNode();
// 2.2 循环遍历,查找待删除节点,找到删除,找不到结束循环
while (currentNode != null) {
if (currentNode.getData() == node.getData()) {
// (1) 设置上一节点的next节点为下一节点
currentNode.getPreNode().setNextNode(currentNode.getNextNode());
// (2) 若下一节点不为null,设置下一节点的pre节点为上一节点
if (currentNode.getNextNode() != null) {
currentNode.getNextNode().setPreNode(currentNode.getPreNode());
}
break;
}
currentNode = currentNode.getNextNode();
}
}
- 获取全部节点的计数
countNodes() -> int
- 遍历统计节点个数
- 返回统计数
/**
* 统计双向链表的有效节点数
* @return int - 有效节点数
*/
public int countNodes() {
// 1. 遍历统计节点个数
// 1.1 设置临时int变量,用来储存节点个数
int count = 0;
// 1.2 设置临时节点变量,用来储存当前变量
DoubleNode currentNode = this.headPointer.getNextNode();
// 1.3 循环遍历,统计所有节点数
while (currentNode != null) {
// count加1
count++;
// 移动到下一节点
currentNode = currentNode.getNextNode();
}
// 2. 返回统计数
return count;
}
- 打印全部节点的数据
displayAllNodes() -> void
/**
* 打印双向链表中的所有节点值
*/
public void displayAllNode() {
// 1. 判断链表是否为空,为空抛出异常
if (this.headPointer.getNextNode() == null) {
throw new RuntimeException("The double linked list is empty!");
}
// 2. 设置节点变量,用来在遍历时储存当前节点
DoubleNode currentNode = this.headPointer.getNextNode();
// 3. 遍历双向链表,打印每个节点值
while (currentNode != null) {
// (1) 打印节点值
System.out.println(currentNode.toString());
// (2) 移动节点到下一位
currentNode = currentNode.getNextNode();
}
}
4. 完整实现
/**
* 双向链表(按节点值从小到大添加)
* @author hoy
*/
public class OrderedDoubleLinkedList {
private DoubleNode headPointer = new DoubleNode(0);
/**
* 添加新节点到双向链表
* @param newNode - 要添加的新节点
*/
public void addNode(DoubleNode newNode) {
// 1. 遍历查找插入点
// 1.1 设置节点变量,用来在遍历时储存当前节点
DoubleNode currentNode = this.headPointer;
// 1.2 遍历双向链表,寻找插入点,找到时结束遍历
while (true) {
// (1) 到达末尾 or 当前节点的next节点值大于新节点值 时,找到插入点(当前值的下一位),结束遍历
if (currentNode.getNextNode() == null || currentNode.getNextNode().getData() > newNode.getData()) {
break;
}
// (2) 找到相同值,抛出异常
else if (currentNode.getNextNode().getData() == newNode.getData()){
throw new RuntimeException("The node is existing!");
}
// (3) 当前节点的next节点值小于新节点值,继续遍历
currentNode = currentNode.getNextNode();
}
// 2. 将新节点插入到当前节点和下一节点之间
// 2.1 设置临时节点变量,用来储存当前节点的下一节点
DoubleNode nextNode = currentNode.getNextNode();
// 2.2 连接当前节点和新节点
currentNode.setNextNode(newNode);
newNode.setPreNode(currentNode);
// 2.3 连接当前节点和next节点
if (nextNode != null) {
newNode.setNextNode(nextNode);
nextNode.setPreNode(newNode);
}
}
/**
* 查找节点是否存在于双向链表中
* @param node - 要查找的节点
* @return boolean - 是否存在
*/
public boolean isNodeExist(DoubleNode node) {
// 1. 若链表为空,抛出异常
if (this.headPointer.getNextNode() == null) {
throw new RuntimeException("The list is empty! ");
}
// 2. 遍历查找相同值
// 2.1 设置boolean变量,用来标识是否找到相同值
boolean found = false;
// 2.2 设置节点变量,用来在遍历时储存当前节点
DoubleNode currentNode = this.headPointer.getNextNode();
// 2.3 循环遍历,查找相同值,找到or到达末尾时,结束遍历
while (currentNode != null) {
// (1) 若当前节点值等于输入节点值,boolean变量设置为true,结束遍历
if (currentNode.getData() == node.getData()) {
found = true;
break;
}
// (2) 否则移动到下一节点,继续循环
currentNode = currentNode.getNextNode();
}
// 3. 返回boolean变量
return found;
}
/**
* 统计双向链表的有效节点数
* @return int - 有效节点数
*/
public int countNodes() {
// 1. 遍历统计节点个数
// 1.1 设置临时int变量,用来储存节点个数
int count = 0;
// 1.2 设置临时节点变量,用来储存当前变量
DoubleNode currentNode = this.headPointer.getNextNode();
// 1.3 循环遍历,统计所有节点数
while (currentNode != null) {
// count加1
count++;
// 移动到下一节点
currentNode = currentNode.getNextNode();
}
// 2. 返回统计数
return count;
}
/**
* 删除双向链表中的某节点
* @param node -- 要删除的节点
*/
public void deleteNode(DoubleNode node) {
// 1. 若链表为空,抛出异常
if (this.headPointer.getNextNode() == null) {
throw new RuntimeException("The list is empty");
}
// 2. 遍历查找待删除节点,找到后删除
// 2.1 设置节点变量,用来在遍历时储存当前节点
DoubleNode currentNode = this.headPointer.getNextNode();
// 2.2 循环遍历,查找待删除节点,找到删除,找不到结束循环
while (currentNode != null) {
if (currentNode.getData() == node.getData()) {
// (1) 设置上一节点的next节点为下一节点
currentNode.getPreNode().setNextNode(currentNode.getNextNode());
// (2) 若下一节点不为null,设置下一节点的pre节点为上一节点
if (currentNode.getNextNode() != null) {
currentNode.getNextNode().setPreNode(currentNode.getPreNode());
}
break;
}
currentNode = currentNode.getNextNode();
}
}
/**
* 打印双向链表中的所有节点值
*/
public void displayAllNode() {
// 1. 判断链表是否为空,为空抛出异常
if (this.headPointer.getNextNode() == null) {
throw new RuntimeException("The double linked list is empty!");
}
// 2. 设置节点变量,用来在遍历时储存当前节点
DoubleNode currentNode = this.headPointer.getNextNode();
// 3. 遍历双向链表,打印每个节点值
while (currentNode != null) {
// (1) 打印节点值
System.out.println(currentNode.toString());
// (2) 移动节点到下一位
currentNode = currentNode.getNextNode();
}
}
}
class DoubleNode {
private int data;
private DoubleNode nextNode;
private DoubleNode preNode;
public DoubleNode(int data) {
this.data = data;
}
@Override
public String toString() {
return "DoubleNode{" +
"data=" + data +
'}';
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public DoubleNode getNextNode() {
return nextNode;
}
public void setNextNode(DoubleNode nextNode) {
this.nextNode = nextNode;
}
public DoubleNode getPreNode() {
return preNode;
}
public void setPreNode(DoubleNode preNode) {
this.preNode = preNode;
}
}