Java数据结构-双向链表的实现

🍵前言:

         好了,栈的博客只是一个小小的热身,现在才是真正的想要展现给大家的,Java数据结构之双向链表,之前,我们都了解过了单向链表,知道了单向链表的增删插改遍历等操作,了解了它们什么时候需要找前一个元素,什么时候不需要,但是双向链表和前面的有所不同,让我们开始吧!

       

         我们将从以下几点开始阐述:

         双向链表的概述 🔜 单双链表之间的区别 🔜 双向链表的实现思路 🔜 双向链表的代码实现与分析 🔜 结论

         喜欢博主的话,那就喜欢吧,不装了,摆烂了

         往期精彩:

                        栈的实现

        下一次博客发出的时间可能是一个星期后了,应为博主要写排序算法全解,篇幅超长,敬请期待!!!


🍘双向链表的概述 

🥟双向链表

  1. 双向链表是基于单向链表的一个升级版链表,他也是一个有序列表
  2. 双向链表可以有头节点,也可以没有头节点

🥣节点 

  1. 节点有三大属性,分别是next域,previous域,data域
  2. next放下一个节点的内存地址,属于has a 的关系
  3. pre放上一个节点的内存地址,属于has a 的关系 

🥨单双链表之间的区别

  • 单向链表只能从头节点开始遍历单向链表,而双向链表可以双向遍历
  • 单向链表在删除元素时,需要找到待删除节点的上一个节点,非常的不方便
  • 双向链表在删除元素时,可以直接找到待删除的节点进行删除即可 

🍿双向链表的实现思路

  • 创建一个双向链表的节点类,该节点中应该有(next,pre,data)(有参构造+无参构造)
  • 创建一个双向链表类,用于管理双向链表,应该有节点属性与其对应的构造方法
  • 添加增删插改遍历的方法 

 注意:本双向链表的创建以水浒英雄的信息的基础上进行创建 

🍦双向链表的代码实现与分析 

  • 创建一个节点类 

 public int no;
    public String name;
    public String nickName;
    public HeroNodeD next;
    public HeroNodeD pre;

    public HeroNodeD() {

    }

    public HeroNodeD(int no,String name,String nickName) {
        this.no = no;
        this.name = name;
        this.nickName = nickName;
    }

    public String toString() {
        return "好汉的排行[" + no + "] 名字:" + name + " 称谓:" + nickName;
    }

 代码分析:

  1. name,nickName,no都是data数据,而next存放的是下一个节点的内存地址,pre存放的是上一个节点的内存地址
  2. 无参构造的目的是创建一个头节点,因为头节点里面的data大多数情况下都为空
  3. 重写toString方法,为了后期输出节点对象 

 图解: 

  •  创建一个双向链表类,用于管理双向链表

private HeroNodeD head;
public DoubleLinkedList() {
        head = new HeroNodeD();
    }

 代码分析:

  1. head为头节点属性,需要通过构造方法给他new一个头节点 
  • 创建一个添加节点的方法

public void add(HeroNodeD nodeD) {
        var cur = head;
        while(cur.next != null) {
            cur = cur.next;
        }
        cur.next = nodeD;
        nodeD.pre = cur;
    }

  代码分析:

  1. 由于不确定双向链表是否为空 ,所以辅助引用需要用head赋值,否则可能出现空指针异常
  2. 通过循环,我们可以找到最后一个节点
  3. 添加节点的时候,记得pre和next都要赋值
  4. 补充:双向链表的头节点是整个链表的灵魂,不能直接使用,需要用一个辅助引用

 图解: 

 

  • 创建一个删除节点的方法

public void del(int no) {
        if(head.next == null) {
            System.out.println("链表为空,无法删除");
            return;
        }
        var cur = head.next;
        boolean flag = false;
        while(true) {
            if(cur == null) {
                break;
            }
            if(cur.no == no) {
                flag = true;
                break;
            }
            cur = cur.next;
        }
        if(flag) {
            cur.pre.next = cur.next;
            if (cur.next != null)
                cur.next.pre = cur.pre;
            return;
        }
        System.out.println("无此节点,无法删除");
    }

   代码分析:

  1. 第一步是判断一下双向链表是否为空,若为空直接退出(校验思想)
  2. 若第一个判断为false,说明双向链表不为空,可以给辅助引用赋值head,next即第一个节点
  3. 打一布尔标记,默认该节点不存在
  4. 建立循环,如果cur=null,说明已经遍历完链表,直接退出
  5. 如果cur.no = no 说明找到了待删除的节点,我们可以修改布尔标记并退出,否则继续往下找
  6. 根据布尔标记做出修改

  图解: 

  • 创建一个插入节点的方法

public void insert(HeroNodeD newNode) {
        if(head.next == null) {
            head.next = newNode;
            newNode.pre = head;
            return;
        }
        boolean flag = false;
        var cur = head;
        while(true) {
            if(cur.next == null) {
                break;
            }
            if(cur.next.no > newNode.no){
                break;
            } else if(cur.next.no == newNode.no){
                flag = true;
                break;
            }
            cur = cur.next;
        }
        if(!flag) {
            newNode.next = cur.next;
            newNode.pre = cur;
            if(cur.next != null){
                newNode.next.pre = newNode;
            }
            cur.next = newNode;
            return;
        }
        System.out.println("已有此节点,无法插入");
    }

    代码分析:

  1. 先判断双向链表是否为空,若为空直接在后面添加即可
  2. 打一布尔标记,默认插入的位置不存在
  3. 插入节点的时候仍要找到该节点的上一个节点,所以,辅助引用应该被赋值为head
  4. 循环,当该节点的next域为空时,直接插入到最后,直接退出
  5. 如果该节点的下一个节点的no大于所给的no,说明这两个节点之间可以插入,直接退出,如果该节点的下一个节点的no等于所给no,说明无法插入,该节点已经存在,,应该改布尔标记并直接退出,否则跳入下一个节点
  6. 根据布尔标记进行插入

   图解: 

 

  • 创建一个更新节点的方法

public void update(HeroNodeD newNode) {
        if(head.next == null) {
            System.out.println("链表为空,无法更新数据");
            return;
        }
        boolean flag = false;
        var cur = head.next;
        while(true) {
            if(cur == null) {
                break;
            }
            if(cur.no == newNode.no){
                flag = true;
                break;
            }
            cur = cur.next;
        }
        if(flag) {
            cur.name = newNode.name;
            cur.nickName = newNode.nickName;
            return;
        }
        System.out.println("无该排名节点,无法更新");
    }

    代码分析: 

  1.  先判断双向链表是否为空,若为空直接结束,无法修改任何的节点(校验意识)
  2.  打一布尔标记,默认该节点不存在
  3.  若第一个条件为false,说明双向链表不为空,所以可以将第一个节点赋予辅助引用
  4.  若cur = null.说明所有的节点都遍历过,可以直接break,否则空指针异常
  5.  若对应的no相同,说明找到了节点,将布尔标记更改并退出,否则跳入下一个节点
  6.  根据布尔标记进行修改
  • 创建一个遍历节点的方法

public void print() {
        if(head.next == null) {
            System.out.println("链表为空,无法遍历");
        }
        var cur = head.next;
        while(cur != null) {
            System.out.println(cur);
            cur = cur.next;
        }
    }

    代码分析: 

  1. 先判断双向链表是否为空,若为空则直接跳出,若不为空辅助引用应该被赋予第一个节点
  2. 我相信大家都可以看得懂的啦 

完整代码 

package datastructure.chapter01.linked_list.double_linked_list;

public class DoubleLinkedList {
    private HeroNodeD head;

    public HeroNodeD getHead() {
        return head;
    }

    public DoubleLinkedList() {
        head = new HeroNodeD();
    }

    public void add(HeroNodeD nodeD) {
        var cur = head;
        while(cur.next != null) {
            cur = cur.next;
        }
        cur.next = nodeD;
        nodeD.pre = cur;
    }

    public void del(int no) {
        if(head.next == null) {
            System.out.println("链表为空,无法删除");
            return;
        }
        var cur = head.next;
        boolean flag = false;
        while(true) {
            if(cur == null) {
                break;
            }
            if(cur.no == no) {
                flag = true;
                break;
            }
            cur = cur.next;
        }
        if(flag) {
            cur.pre.next = cur.next;
            if (cur.next != null)
                cur.next.pre = cur.pre;
            return;
        }
        System.out.println("无此节点,无法删除");
    }

    public void print() {
        if(head.next == null) {
            System.out.println("链表为空,无法遍历");
        }
        var cur = head.next;
        while(cur != null) {
            System.out.println(cur);
            cur = cur.next;
        }
    }

    public void update(HeroNodeD newNode) {
        if(head.next == null) {
            System.out.println("链表为空,无法更新数据");
            return;
        }
        boolean flag = false;
        var cur = head.next;
        while(true) {
            if(cur == null) {
                break;
            }
            if(cur.no == newNode.no){
                flag = true;
                break;
            }
            cur = cur.next;
        }
        if(flag) {
            cur.name = newNode.name;
            cur.nickName = newNode.nickName;
            return;
        }
        System.out.println("无该排名节点,无法更新");
    }

    public void insert(HeroNodeD newNode) {
        if(head.next == null) {
            head.next = newNode;
            newNode.pre = head;
            return;
        }
        boolean flag = false;
        var cur = head;
        while(true) {
            if(cur.next == null) {
                break;
            }
            if(cur.next.no > newNode.no){
                break;
            } else if(cur.next.no == newNode.no){
                flag = true;
                break;
            }
            cur = cur.next;
        }
        if(!flag) {
            newNode.next = cur.next;
            newNode.pre = cur;
            if(cur.next != null){
                newNode.next.pre = newNode;
            }
            cur.next = newNode;
            return;
        }
        System.out.println("已有此节点,无法插入");
    }
}

 

 🍔结论

        本篇博客主要阐述了双向链表的创建与其功能的完善,特此,我来总结一下必须掌握的几点

        双向链表什么时候找前一个节点什么时候找当前节点

        双向链表删除节点的情况如何

        防止空指针异常的举措 

        下一站:一万年以后

  • 12
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爪哇土著、JOElib

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值