单向链表的缺点分析

  • 查找的方向只能是一个方向,而双向链表可以向前或者向后查找;
  • 单向链表不能自我删除,需要靠辅助节点,单链表删除节点时,总是找到temp节点(待删除节点的前一个节点)辅助操作;

    双向链表操作分析

    image.png
    双向链表的遍历和修改与单链表一样,只是遍历时可以向前也可以向后;
  1. 添加(添加到最后)
  • 先找到双向链表的最后这个节点 temp
  • temp.next = newHeroNode; newHeroNode.pre = temp;
  1. 删除
  • 找到要删除的这个节点 temp
  • temp.pre.next = temp.next; temp.next.pre = temp.pre;

双链表操作实现

1. 节点定义

  1. package com.zsy.datastructure.linkedlist.doublelinklist;
  2. /**
  3. * 以水浒英雄信息作为双向链表的节点
  4. *
  5. * @author zhangshuaiyin
  6. */
  7. public class HeroNode {
  8. int no;
  9. String name;
  10. String nickName;
  11. // next: 指向下一个节点,默认为null
  12. HeroNode next;
  13. // pre: 指向前一个节点,默认为null
  14. HeroNode pre;
  15. public HeroNode(int no, String name, String nickName) {
  16. this.no = no;
  17. this.name = name;
  18. this.nickName = nickName;
  19. }
  20. @Override
  21. public String toString() {
  22. return "HeroNode{" +
  23. "no=" + no +
  24. ", name='" + name + '\'' +
  25. ", nickName='" + nickName + '\'' +
  26. '}';
  27. }
  28. }

2. 双向链表操作方法

  1. package com.zsy.datastructure.linkedlist.doublelinklist;
  2. /**
  3. * 单链表存储水浒英雄节点信息
  4. *
  5. * @author zhangshuaiyin
  6. */
  7. public class DoubleLinkedList {
  8. /**
  9. * head 节点,指向链表第一个节点,定位链表地址
  10. */
  11. private final HeroNode head = new HeroNode(0, "", "");
  12. /**
  13. * @param node 添加节点:从链表最后
  14. */
  15. public void add(HeroNode node) {
  16. HeroNode temp = head;
  17. while (temp.next != null) {
  18. temp = temp.next;
  19. }
  20. temp.next = node;
  21. node.pre = temp;
  22. }
  23. /**
  24. * 按照编号由小到大的顺序插入节点,编号不可重复
  25. *
  26. * @param node 插入节点
  27. */
  28. public void addByOrder(HeroNode node) {
  29. HeroNode temp = head;
  30. while (temp.next != null) {
  31. if (temp.next.no == node.no) {
  32. System.out.printf("编号 %d 已存在,插入失败\n", node.no);
  33. return;
  34. } else if (temp.next.no > node.no) {
  35. // 此时说明新节点应该插入 temp 的下一个位置
  36. break;
  37. }
  38. temp = temp.next;
  39. }
  40. //退出时代表temp已到达需要插入的节点的位置的
  41. //插入节点的下一个节点为temp的下一个节点
  42. node.next = temp.next;
  43. // temp.next = null 说明插入的是第一个节点
  44. if (temp.next != null) {
  45. // 设置插入位置的上一个节点
  46. temp.next.pre = node;
  47. }
  48. //将temp的下一个节点更改为新插入的节点
  49. temp.next = node;
  50. //设置插入节点的上一个节点
  51. node.pre = temp;
  52. }
  53. /**
  54. * 根据新节点信息修改链表中相同编号的节点
  55. *
  56. * @param node 新节点
  57. */
  58. public void update(HeroNode node) {
  59. HeroNode temp = head.next;
  60. while (temp != null) {
  61. if (temp.no == node.no) {
  62. temp.name = node.name;
  63. temp.nickName = node.nickName;
  64. return;
  65. }
  66. temp = temp.next;
  67. }
  68. System.out.printf("没有找到编号是 %d 的节点,不能修改\n", node.no);
  69. }
  70. /**
  71. * @param no 根据编号 no 删除链表中对应的节点
  72. */
  73. public void delete(int no) {
  74. HeroNode temp = head.next;
  75. while (temp != null) {
  76. if (temp.no == no) {
  77. temp.pre.next = temp.next;
  78. if (temp.next != null) {
  79. temp.next.pre = temp.pre;
  80. }
  81. return;
  82. }
  83. temp = temp.next;
  84. }
  85. System.out.printf("没有找到编号是 %d 的节点,不能修改\n", no);
  86. }
  87. /**
  88. * 打印链表信息
  89. */
  90. public void list() {
  91. if (head.next == null) {
  92. throw new RuntimeException("当前链表为空");
  93. }
  94. HeroNode temp = head.next;
  95. while (temp != null) {
  96. System.out.println(temp);
  97. temp = temp.next;
  98. }
  99. }
  100. }

3. 测试方法

  1. package com.zsy.datastructure.linkedlist.doublelinklist;
  2. /**
  3. * @author zhangshuaiyin
  4. * @date 2021-04-06 14:33
  5. */
  6. public class DoubleLinkedListTest {
  7. static HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
  8. static HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
  9. static HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
  10. static HeroNode hero4 = new HeroNode(4, "公孙胜", "入云龙");
  11. static DoubleLinkedList linkedList = new DoubleLinkedList();
  12. public static void main(String[] args) {
  13. System.out.println("-----------------从链表最后添加节点:---------------------");
  14. testAdd();
  15. linkedList = new DoubleLinkedList();
  16. System.out.println("-----------------按节点编号大小添加:---------------------");
  17. testAddByOrder();
  18. System.out.println("-----------------按节点编号修改节点:---------------------");
  19. HeroNode newNode = new HeroNode(2, "Lu JunYi", "Yu QiLin");
  20. testUpdate(newNode);
  21. System.out.println("-----------------按节点编号删除节点:---------------------");
  22. testDelete();
  23. }
  24. /**
  25. */
  26. private static void testDelete() {
  27. linkedList.delete(4);
  28. linkedList.list();
  29. }
  30. /**
  31. * @param newNode 更新节点
  32. */
  33. private static void testUpdate(HeroNode newNode) {
  34. linkedList.update(newNode);
  35. linkedList.list();
  36. }
  37. /**
  38. * 测试按编号大小添加元素
  39. */
  40. private static void testAddByOrder() {
  41. linkedList.addByOrder(hero1);
  42. linkedList.addByOrder(hero4);
  43. linkedList.addByOrder(hero2);
  44. linkedList.addByOrder(hero3);
  45. linkedList.list();
  46. }
  47. /**
  48. * add() 方法按照添加顺序从最后添加
  49. */
  50. public static void testAdd() {
  51. linkedList.add(hero1);
  52. linkedList.add(hero4);
  53. linkedList.add(hero2);
  54. linkedList.add(hero3);
  55. linkedList.list();
  56. }
  57. }