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()); }}