与单项链表,双向链表在反向遍历的时候更方便
所以
它不仅有 next域指向下一个节点
还有 pre 域指向前一个节点

  1. class Node {
  2. int no;
  3. String name;
  4. String nickName;
  5. // 指向前一个节点
  6. Node pre;
  7. // 指向下一个节点
  8. Node next;
  9. public Node(int no, String name, String nickName) {
  10. this.no = no;
  11. this.name = name;
  12. this.nickName = nickName;
  13. }
  14. @Override
  15. public String toString() {
  16. return "[HeroNode no=" + no + ", name=" + name + ", nickName=" + nickName + "]";
  17. }
  18. }

双向链表的基本操作

双向链表的基本操作里只有少许操作发生了变化,其余可参考单链表

list和 update

这两个操作基本没有发生变化

  1. // 遍历双向链表
  2. public void list() {
  3. if (head.next == null) {
  4. System.out.println("当前链表为空");
  5. return;
  6. }
  7. // 这里我们不需要输出头节点,所以我们可以直接让辅助指针指向第一个节点
  8. Node temp = head.next;
  9. while (true) {
  10. // 这里因为我们要输出最后一个元素,所以不能 判断 temp.next == null
  11. if (temp == null) {
  12. break;
  13. }
  14. System.out.println(temp);
  15. temp = temp.next;
  16. }
  17. }
  18. // 根据节点的 no进行修改
  19. public void update(Node node) {
  20. if (head.next == null) {
  21. System.out.println("链表为空");
  22. return;
  23. }
  24. Node temp = head.next;
  25. // 用来判断是否找到 要修改节点
  26. boolean flag = false;
  27. // 找到需要修改的节点
  28. while (true) {
  29. if (temp == null) {
  30. // 已经遍历完成
  31. break;
  32. }
  33. if (temp.no == node.no) {
  34. // 找到该节点
  35. flag = true;
  36. break;
  37. }
  38. temp = temp.next;
  39. }
  40. if (flag) {
  41. temp.name = node.name;
  42. temp.nickName = node.nickName;
  43. } else {
  44. System.out.println("没有找到编号为" + node.no + "节点");
  45. }
  46. }

add

add 的时候记得要 将 新加入的节点 node 指向前一个节点 temp
也就是
temp.next = node
node.pre = temp

  1. // 向双向链表中添加节点
  2. // 向链表中添加新元素
  3. public void add(Node node) {
  4. // 用一个临时的辅助指针来代替 头指针进行遍历
  5. Node temp = head;
  6. // 循环遍历到最后一个节点
  7. while (true) {
  8. // 如果temp.next == null,说明当前节点为最后一个节点
  9. if (temp.next == null) {
  10. break;
  11. }
  12. // temp 指针后移,指向下一个节点
  13. temp = temp.next;
  14. }
  15. // 将最后一个节点 next指向新的元素
  16. temp.next = node;
  17. // 将新节点的 pre 指向前一个节点
  18. node.pre = temp;
  19. }

delete 变化

delete 变化最大,我们不需要向单链表一样找到被删节点的前一个节点了,
因为在双向链表中,被删节点本身就有前一个节点和后一个节点的信息
所以代码上我们的变化会变得比较大
具体思路为,使用辅助接点temp 找到被删节点node
temp.no == node.no

然后让被删节点的 前一个节点的next 指向 后一个节点
temp.pre.next = temp.next;
并且让后一个节点的pre域指向被删节点的前一个节点,
这里需要判断被删节点是不是最后一个节点
如果不判断的话,当temp是最后一个节点时,会报空指针异常
if (temp.next != null) {
temp.next.pre = temp.pre;
}

  1. // 删除一个节点
  2. public void delete(Node node) {
  3. if (head.next == null) {
  4. System.out.println("链表为空");
  5. return;
  6. }
  7. Node temp = head.next;
  8. boolean flag = false;
  9. while (true) {
  10. // 关于这里是 temp == null
  11. // 还是 temp.next == null
  12. // 关键看你需不需要对最后一个节点参与循环
  13. // temp 是参与循环, temp.next 不参与
  14. if (temp == null) {
  15. break;
  16. }
  17. if (temp.no == node.no) {
  18. // 找到该节点,不必像单链表那样找到前一个节点
  19. flag = true;
  20. break;
  21. }
  22. temp = temp.next;
  23. }
  24. if (flag) {
  25. temp.pre.next = temp.next;
  26. // 如果不判断的话,当temp是最后一个节点时,会报空指针异常
  27. if (temp.next != null) {
  28. temp.next.pre = temp.pre;
  29. }
  30. } else {
  31. System.out.println("没有找到编号为" + node.no + "节点");
  32. }
  33. }

addByOrder 升序

这个思路也发生变化,之前也是寻找前一个节点,但是现在我们同样可以找到被插入位置的节点
本来是想在 while 循环中就进行 插入
但是有一个问题就是,如果被插入的节点是最后一个节点,那么它不会被循环到,
如果将while的终止条件变成 temp == null 的话,退出循环后又会空指针异常,
所以我这里在最后又判断了一次node.no > temp.no 来保证 最后一个节点也能被插入,
这个代码可能不是最好的,但是代码是前几天实现的,现在有点懒的优化了。

  1. public void addByOrder(Node node) {
  2. // 用一个临时的辅助指针来代替 头指针进行遍历
  3. Node temp = head;
  4. boolean flag = false;
  5. // 循环遍历到最后一个节点
  6. while (true) {
  7. // 如果temp.next == null,说明当前节点为最后一个节点
  8. if (temp.next == null) {
  9. break;
  10. }
  11. if (node.no < temp.no) {
  12. flag = true;
  13. break;
  14. }
  15. // temp 指针后移,指向下一个节点
  16. temp = temp.next;
  17. }
  18. //正常添加
  19. if (flag == false && node.no > temp.no) {
  20. // 将最后一个节点 next指向新的元素
  21. temp.next = node;
  22. // 将新节点的 pre 指向前一个节点
  23. node.pre = temp;
  24. } else {
  25. temp.pre.next = node;
  26. node.pre = temp.pre;
  27. node.next = temp;
  28. temp.pre = node;
  29. }
  30. }