1. 链表对象
// 定义 HeroNode2 , 每个 HeroNode 对象就是一个节点
class HeroNode {
public String no;
public String name;
public String nickname;
public HeroNode next; // 指向下一个节点, 默认为 null
public HeroNode pre; // 指向前一个节点, 默认为 null
// 构造器
public HeroNode(String no, String name, String nickname) {
this.no = no;
this.name = name;
this.nickname = nickname;
}
// 为了显示方法,我们重新 toString
@Override
public String toString() {
return "HeroNode [no=" + no + ", name=" + name + ", nickname=" + nickname + "]";
}
}
2. 对链表对象进行操作
// 创建一个双向链表的类
class DoubleLinkedList {
// 先初始化一个头节点, 头节点不要动, 不存放具体的数据
private HeroNode head = new HeroNode("0", "", "");
//定义双向链表的尾节点
private HeroNode last;
// 返回头节点
public HeroNode getHead() {
return head;
}
// 遍历双向链表的方法
// 显示链表[遍历]
public List<uhjcTreeDetailVO> list() {
List<uhjcTreeDetailVO> list = new ArrayList<>();
// 判断链表是否为空
if (head.next == null) {
System.out.println("链表为空");
return null;
}
// 因为头节点,不能动,因此我们需要一个辅助变量来遍历
HeroNode temp = head.next;
while (true) {
// 判断是否到链表最后
if (temp == null) {
break;
}
// 设置
uhjcTreeDetailVO uhjcTreeDetailVO = new uhjcTreeDetailVO();
uhjcTreeDetailVO.setNodeId(temp.name);
uhjcTreeDetailVO.setNextNodeId(temp.nickname);
list.add(uhjcTreeDetailVO);
// 输出节点的信息
System.out.println(temp);
// 将 temp 后移, 一定小心
temp = temp.next;
}
return list;
}
// 添加一个节点到双向链表的最后.
public void addLast(HeroNode heroNode) {
// 因为 head 节点不能动,因此我们需要一个辅助遍历 temp
HeroNode temp = head;
// 遍历链表,找到最后
while (true) {
// 找到链表的最后
if (temp.next == null) {
break;
}
// 如果没有找到最后, 将将 temp 后移
temp = temp.next;
}
// 当退出 while 循环时,temp 就指向了链表的最后
// 形成一个双向链表
temp.next = heroNode;
heroNode.pre = temp;
}
//头插法
public void addFirst(HeroNode heroNode) {
//定义一个用作插入的节点
//1.假设双向链表为空
if (this.head == null) {
this.head = heroNode;
this.last = heroNode;
} else {
//2.双向链表不为空的情况下
//不懂请看上面的图解,就很简单了
heroNode.next = this.head;
this.head.pre = heroNode;
this.head = heroNode;
}
}
//在任意位置插入
public void addIndex(int index, HeroNode heroNode) {//形参index为插入元素位置,data为插入的数值
//定义一个用作插入的节点
HeroNode cur = this.head;//定义一个cur用作遍历双向链表
//1、判断插入位置的合法性
if (index < 0 || index > size()) {
System.out.println("插入位置不合法!!!");
return;
}
//2、假设插入位置为头结点的情况下,即就是头插法
if (index == 0) {
addFirst(heroNode);//调用头插法代码
return;
}
//3、假设插入位置为尾结点的情况下,即就是尾插法
if (index == size()) {
addLast(heroNode);//调用尾插法代码
return;
}
//4、在中间任意位置的情况下
while (index != 0) {
cur = cur.next;
index--;
}
//不懂请看上面的图解,就很简单了
//核心代码
heroNode.next = cur;
heroNode.pre = cur.pre;
cur.pre = heroNode;
heroNode.pre.next = heroNode;
}
//在任意位置查询
public HeroNode findIndex(int index) {
//定义一个用作查询的节点
HeroNode cur = this.head;//定义一个cur用作遍历双向链表
//1、判断插入位置的合法性
if (index < 0 || index > size()) {
System.out.println("查询位置不合法!!!");
return null;
}
//2、判断是否为头部
if (index == 0) {
return this.head;
}
//3、判断是否为尾部
if (index == size()) {
return this.last;
}
//4、在中间任意位置的情况下
while (index != 0) {
cur = cur.next;
index--;
}
return cur;
}
//求双向链表的长度(之后addIndex代码会用到)
public int size() {
int count = 0;
HeroNode cur = this.head;
while (cur != null) {
count++;
cur = cur.next;
}
return count;
}
// 修改一个节点的内容, 可以看到双向链表的节点内容修改和单向链表一样
// 只是 节点类型改成 HeroNode2
public void update(HeroNode newHeroNode) {
// 判断是否空
if (head.next == null) {
System.out.println("链表为空~");
return;
}
// 找到需要修改的节点, 根据 no 编号
// 定义一个辅助变量
HeroNode temp = head.next;
boolean flag = false; // 表示是否找到该节点
while (true) {
if (temp == null) {
break; // 已经遍历完链表
}
if (newHeroNode.no.equals(temp.no)) {
// 找到
flag = true;
break;
}
temp = temp.next;
}
// 根据 flag 判断是否找到要修改的节点
if (flag) {
temp.name = newHeroNode.name;
temp.nickname = newHeroNode.nickname;
} else { // 没有找到
System.out.printf("没有找到 编号 %d 的节点,不能修改\n", newHeroNode.no);
}
}
// 从双向链表中删除一个节点,
// 说明
// 1 对于双向链表,我们可以直接找到要删除的这个节点
// 2 找到后,自我删除即可
public void del(String no) {
// 判断当前链表是否为空
if (head.next == null) {
// 空链表
System.out.println("链表为空,无法删除");
return;
}
HeroNode temp = head.next;
// 辅助变量(指针)
boolean flag = false;
// 标志是否找到待删除节点的
while (true) {
if (temp == null) { // 已经到链表的最后
break;
}
if (temp.no == no) {
// 找到的待删除节点的前一个节点temp
flag = true;
break;
}
temp = temp.next; // temp 后移,遍历
}
// 判断 flag
if (flag) { // 找到
// 可以删除
temp.next = temp.next.next;
//[单向链表]
temp.pre.next = temp.next;
// 这里我们的代码有问题?
// 如果是最后一个节点,就不需要执行下面这句话,否则出现空指针
if (temp.next != null) {
temp.next.pre = temp.pre;
}
} else {
System.out.printf("要删除的 %d 节点不存在\n", no);
}
}
}
3. 测试
public class DoubleLinkedListDemo {
public static void main(String[] args) {
// 测试
System.out.println("双向链表的测试");
// 先创建节点\
HeroNode hero1 = new HeroNode("1", "1", "0");
HeroNode hero2 = new HeroNode("2", "2", "玉麒麟");
HeroNode hero3 = new HeroNode("3", "吴用", "智多星");
HeroNode hero4 = new HeroNode("4", "林冲", "豹子头");
// 创建一个双向链表
DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
doubleLinkedList.addLast(hero1);
doubleLinkedList.addLast(hero2);
doubleLinkedList.addLast(hero3);
doubleLinkedList.addLast(hero4);
doubleLinkedList.list();
// 修改
HeroNode newHeroNode = new HeroNode("4", "公孙胜", "入云龙");
doubleLinkedList.update(newHeroNode);
System.out.println("修改后的链表情况");
List<uhjcTreeDetailVO> list = doubleLinkedList.list();
list.forEach(uhjcTreeDetailVO -> {
});
// 删除
doubleLinkedList.del("3");
System.out.println("删除后的链表情况~~");
doubleLinkedList.list();
// 随机插入
System.out.println("随机插入");
doubleLinkedList.addIndex(1, hero3);
doubleLinkedList.list();
// 查询指定节点
System.out.println("随机查询");
HeroNode index = doubleLinkedList.findIndex(1);
System.out.println(index.toString());
}
}