• 【数据结构】—— 双链表的增删改查


    ❤️一名热爱Java的大一学生,希望与各位大佬共同学习进步❤️

    🧑个人主页:@周小末天天开心

    各位大佬的点赞👍 收藏⭐ 关注✅,是本人学习的最大动力

    感谢!

    📕该篇文章收录专栏—数据结构

    目录

    双链表

    单链表和双链表的区别

    双链表的增删改查

    定义HeroNode2类,用来存放属性

    创建DoubleLinkedList类用来存放方法

    添加节点到链表最后

    按照编号的顺序添加节点

    显示双向链表

    修改链表中的节点信息

    删除节点信息

    编写DoubleLinkedListDemo类进行演示


    双链表

            双链表也叫双向链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。

    单链表和双链表的区别

    (1)单链表查找的方向只能是一个方向,而双链表可以向前或者向后查找

    (2)单链表不能自我删除,需要依靠辅助节点,而双链表可以自我删除(单链表删除时,总是要找到辅助节点temp,temp是待删除节点的前一个节点)

    (3)双链表比单链表多了一个 pre 属性,用来指向前一个节点,默认为null


    双链表的增删改查

    定义HeroNode2类,用来存放属性

    1. //定义HeroNode2 ,每个HeroNode2 对象就是一个节点
    2. class HeroNode2 {
    3. public int no;
    4. public String name;
    5. public String nickname;//昵称
    6. public HeroNode2 next;//指向下一个节点,默认为null
    7. public HeroNode2 pre;//指向前一个节点,默认为null
    8. //构造器
    9. public HeroNode2(int no, String name, String nickname) {
    10. this.no = no;
    11. this.name = name;
    12. this.nickname = nickname;
    13. }
    14. //为了显示方法,这里重写 toString() 方法
    15. @Override
    16. public String toString() {
    17. return "HeroNode{" +
    18. "no=" + no +
    19. ", name='" + name + '\'' +
    20. ", nickname='" + nickname + '\'' + "}";
    21. }
    22. }

    创建DoubleLinkedList类用来存放方法

    添加节点到链表最后

    (1)先创建一个 head头节点,作用就是表示单链表的头

    (2)找到双向链表的最后一个节点

    (3)temp.next = heroNode;

    (4)heroNode.pre = temp;

    1. //创建一个双向链表的类
    2. class DoubleLinkedList {
    3. //先初始化一个头节点,头节点不要动,如果改动头节点那么就不好去找到链表最顶端的节点了
    4. private HeroNode2 head = new HeroNode2(0, "", "");
    5. //头节点不存放具体的数据
    6. public HeroNode2 getHead() {//返回头节点
    7. return head;
    8. }
    9. //添加数据到双链表最后
    10. public void add(HeroNode2 heroNode) {
    11. //因为head头节点不能动,所以需要一个辅助变量temp
    12. HeroNode2 temp = head;//将temp指向head
    13. //遍历链表,找到链表最后
    14. while (true) {
    15. //当temp的域等于空时,说明temp找到了链表最后了
    16. if (temp.next == null) {//找到最后了,结束程序
    17. break;
    18. }
    19. //如果没有找到最后,就将temp 后移指向下一个数据
    20. temp = temp.next;
    21. }
    22. //当退出了 while 循环时,那么 temp 就指向了链表的最后
    23. temp.next = heroNode; //将最后这个节点的next域指向新的节点
    24. heroNode.pre = temp;//将最后这个节点指向前一个节点,构成双链表
    25. }
    26. }

    按顺序添加到链表后演示

     

    按照编号的顺序添加节点

    1. //根据排名将英雄插入到指定位置
    2. public void addByOrder(HeroNode2 heroNode) {
    3. //因为头节点不能动,因此需要通过一个辅助指针(变量)来帮助找到添加的位置
    4. //找到辅助变量,辅助变量temp应该是位于添加位置的前一个节点,否则无法插入
    5. HeroNode2 temp = head;
    6. boolean flag = false;//flag标志添加的编号是否存在,默认为false
    7. while(true) {
    8. if(temp.next == null) {//说明temp已经到了链表最后
    9. break;
    10. }
    11. if(temp.next.no > heroNode.no) {//位置找到,就在temp的后面插入
    12. //如果temp.next.no > heroNode.no
    13. //说明 heroNode 就应该插入到 temp 和 temp.next 之间
    14. break;
    15. }
    16. if(temp.next.no == heroNode.no) {
    17. //说明希望添加的heroNode的编号以及存在
    18. flag = true;
    19. break;
    20. }
    21. temp = temp.next;//如果三个条件都不符合就将 temp 后移,继续遍历
    22. }
    23. //判断flag的值
    24. if(flag) {
    25. //如果 flag == true。说明编号已存在,所以无法插入
    26. System.out.printf("准备插入的编号%d已经存在,无法插入",heroNode.no);
    27. } else {
    28. //否则可以插入到链表中,temp 的后面
    29. if(temp.next != null) {
    30. //如果要插入的节点在最后则不需要执行下面的语句
    31. heroNode.next = temp.next;
    32. temp.next.pre = heroNode;
    33. }
    34. temp.next = heroNode;
    35. heroNode.pre = temp;
    36. }
    37. }

    显示双向链表

    1. //显示双向链表[遍历]
    2. public void list() {
    3. //先判断链表是否为空
    4. if (head.next == null) { //说明了链表为空
    5. System.out.println("链表为空");
    6. return;
    7. }
    8. //如果链表不为空,且头节点不能动,因此需要一个辅助变量来遍历
    9. HeroNode2 temp = head.next;
    10. //因为已经判断了链表不为空,说明链表至少有一个数据,所以是 head.next(头节点指向的下一个数据)
    11. while (true) {
    12. //判断是否已经到了链表最后
    13. if (temp == null) {
    14. //遍历是到了链表最后,所以退出循环
    15. break;
    16. }
    17. //如果不为空,则输出该节点的信息
    18. System.out.println(temp);//已经重写了 toString()
    19. //输出后需要将temp后移一位,因为输出该信息后需要让temp指向下一位,输出下一位的信息
    20. temp = temp.next;//不后移就是一个死循环
    21. }
    22. }

    修改链表中的节点信息

    1. //修改节点的信息,根据no编号来修改,即no编号不能改
    2. //根据newHeroNew 的 no 来进行修改即可
    3. public void update(HeroNode2 newHeroNew) {
    4. //判断是否为空
    5. if (head.next == null) {
    6. System.out.println("该链表为空");
    7. return;
    8. }
    9. //修改节点的信息,根据no编号来修改,即no编号不能改
    10. //根据newHeroNew 的 no 来进行修改即可
    11. //可以看出双向链表的节点内容修改和单向链表一样
    12. //只是节点类型更改为了 HeroNode2
    13. HeroNode2 temp = head.next;//temp 赋的值为头节点的下一个节点
    14. boolean flag = false;//表示是否找到了该节点
    15. while (true) {
    16. if (temp == null) {
    17. //说明链表已经遍历完成,因为temp 指向的是下一个节点(需要注意,和上面不同)
    18. break;
    19. }
    20. if (temp.no > newHeroNew.no) {
    21. //因为temp 指向的是下一个节点。
    22. //所以就不需要 temp.next.no 了(需要注意,和上面不同)
    23. //位置找到,就在temp后面进行添加
    24. break;
    25. }
    26. if (temp.no == newHeroNew.no) {
    27. //因为temp 指向的是下一个节点。
    28. //所以就不需要 temp.next.no 了(需要注意,和上面不同)
    29. //说明希望添加的编号已经存在
    30. flag = true;
    31. break;
    32. }
    33. temp = temp.next;//如果循环没有结束就一直进行遍历
    34. }
    35. //根据 flag 来进行判断是否找到了要修改的节点
    36. if (flag) {//如果flag 为 true 说明就找到了要修改的节点
    37. temp.name = newHeroNew.name;
    38. temp.nickname = newHeroNew.nickname;
    39. //注意:no 编号不可以修改
    40. } else { //说明没有找到要修改的节点
    41. System.out.printf("没有找到编号等于%d的节点\n", newHeroNew.no);
    42. }
    43. }

    修改链表中的节点信息演示

     

    删除节点信息

    (1)因为是双向链表,所以我们可以自我删除某个节点

    (2)直接找到要删除的节点 temp

    (3)temp.pre.next = temp.next;

    (4)temp.next.pre = temp.pre;(如果是删除最后一个节点就不需要,否则会报空指针异常)

    (5)被删除的节点,将不会有其他引用指向,会被垃圾回收机制回收

    1. //删除节点
    2. /*
    3. 思路
    4. 1.head 节点不能动,因此我们需要一个 temp 辅助接点找到待删除的前一个节点
    5. 2.说明我们在比较时,是 temp.next.no 和需要删除的节点的 no 比较
    6. */
    7. public void delete(int no) {
    8. //判断链表是否为空
    9. if (head.next == null) {
    10. System.out.println("链表为空,无法删除");
    11. return;
    12. }
    13. HeroNode2 temp = head.next;//辅助节点
    14. boolean flag = false;//标志是否找到了待删除的节点
    15. while (true) {
    16. if (temp == null) {//已经到链表的最后了
    17. break;
    18. }
    19. if (temp.no == no) {
    20. //找到了待删除节点temp
    21. flag = true;
    22. break;
    23. }
    24. temp = temp.next;//让 temp 后移,实现遍历
    25. }
    26. //判断 flag
    27. if (flag) {
    28. //如果flag为真,找到了要删除的节点,可以删除
    29. temp.pre.next = temp.next;
    30. //如果是删除最后一个节点就不需要执行下面这句话,否则会空指针异常
    31. if (temp.next != null) {
    32. temp.next.pre = temp.pre;
    33. }
    34. } else {
    35. System.out.printf("要删除的%d节点不存在\n", no);
    36. }
    37. }

    删除节点信息演示


    编写DoubleLinkedListDemo类进行演示

    1. public class DoubleLinkedListDemo {
    2. public static void main(String[] args) {
    3. //测试双链表
    4. System.out.println("=====双链表的测试=====");
    5. //先创建节点
    6. HeroNode2 hero1 = new HeroNode2(1, "宋江", "及时雨");
    7. HeroNode2 hero2 = new HeroNode2(2, "卢俊义", "玉麒麟");
    8. HeroNode2 hero3 = new HeroNode2(3, "吴用", "智多星");
    9. HeroNode2 hero4 = new HeroNode2(4, "林冲", "豹子头");
    10. //创建双向链表
    11. DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
    12. //添加双链表中的数据到最后
    13. doubleLinkedList.add(hero1);
    14. doubleLinkedList.add(hero2);
    15. doubleLinkedList.add(hero3);
    16. doubleLinkedList.add(hero4);
    17. //显示双链表
    18. doubleLinkedList.list();
    19. // //插入双链表中的数据
    20. // doubleLinkedList.addByOrder(hero2);
    21. // doubleLinkedList.addByOrder(hero4);
    22. // doubleLinkedList.addByOrder(hero3);
    23. // doubleLinkedList.addByOrder(hero1);
    24. // //显示双链表
    25. // doubleLinkedList.list();
    26. // //测试修改节点后的链表
    27. // System.out.println("\n=====修改后的链表为=====");
    28. // HeroNode2 newHeroNode = new HeroNode2(4, "公孙胜", "入云龙");
    29. // doubleLinkedList.update(newHeroNode);
    30. // //显示修改后的双链表
    31. // doubleLinkedList.list();
    32. // //删除双链表中的节点
    33. // doubleLinkedList.delete(1);
    34. // doubleLinkedList.delete(4);
    35. // //显示删除后的双链表
    36. // System.out.println("\n=====删除后的双链表=====");
    37. // doubleLinkedList.list();
    38. }
    39. }

     

  • 相关阅读:
    DRF: 序列化器、View、APIView、GenericAPIView、Mixin、ViewSet、ModelViewSet的源码解析
    内网资料传外网速度太慢怎么办,一招教你解决
    Java实战:Spring Boot项目Jar包加密
    美团二面:为什么 Redis 会有哨兵?
    羊了个羊品牌域名情况如何?
    karmada介绍和分析
    springMvc整合mybatis-plus
    golang - new和make的区别
    贪吃蛇游戏代码(C语言项目)
    10月最新外贸进出口情况,外贸整体向好
  • 原文地址:https://blog.csdn.net/weixin_71646897/article/details/128018423