数据结构与算法(三):双向链表[通俗易懂]

数据结构与算法(三):双向链表[通俗易懂]一、双向链表双向链表与单链表基本相似,但是最大的区别在于双向链表在节点中除了指向下一节点的next指针外,还有指向前一节点的prev指针,这使得双向链表在可以在任意节点从头尾两个方向进行遍历,是“双

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

一、双向链表

双向链表与单链表基本相似,但是最大的区别在于双向链表在节点中除了指向下一节点的next指针外,还有指向前一节点的prev指针,这使得双向链表在可以在任意节点从头尾两个方向进行遍历,是“双向”的。

和单链表相比,双向链表在删除和查询等方面明显在操作上更具有灵活性,但是会消耗更多的内存,需要根据使用条件进行取舍。

java中的LinkedHashMap的本质即是一个双向链表。

数据结构与算法(三):双向链表[通俗易懂]

二、双向链表的简单实现

修改原来的Node类,在里面添加一个新成员变量Node prev

/**
 * @Author:huang
 * @Date:2020-06-20 10:19
 * @Description:节点类
 */
public class Node {

    //节点序号
    int num;

    //数据
    Object data;

    //下一个节点
    Node next;

    //上一节点
    Node prev;

    public Node(int num, Object data) {
        this.num = num;
        this.data = data;
    }

    @Override
    public String toString() {
        return "Node{" +
            "num=" + num +
            ", data=" + data +
            '}';
    }
}

1.添加

添加与单向链表代码逻辑一样,但是新节点在添加时需要修改prev指针指向原来的尾节点。

举个例子,对于无排序插入,原本有节点A,现在要插入一个B:

  1. 找到A,然后让A.next指向B
  2. B.prev指向A

而对于排序插入,就是原有节点A,C,要在中间插入B:

  1. 找到A,让B.prev指向A
  2. B.next指向A.next,也就是让B的next指向C
  3. A.next.prev指向B,也就是让C的prev指向B
  4. A.next指向B
/**
 * 添加节点到链表
 * @param node 要插入的节点
 */
public void add(Node node) {
    Node temp = head;
    //遍历链表
    while (true) {
        if (temp.next == null) {
            break;
        }
        //不是尾节点就继续遍历下一个节点
        temp = temp.next;
    }
    //将尾节点指向即将插入的新节点
    temp.next = node;
    node.prev = temp;
}

/**
 * 按顺序添加节点到链表
 * @param node 要插入的节点
 */
public void addByOrder(Node node) {
    Node temp = head;
    //遍历链表
    while (true) {
        //如果链表到底了就直接插入
        if (temp.next == null) {
            temp.next = node;
            node.prev = temp;
            return;
        }
        else if (temp.next.num > node.num){
            //如果后一节点比当新节点大,就插入当前节点
            node.prev = temp;
            node.next = temp.next;

            temp.next.prev = node;
            temp.next = node;
            return;
        }else if (temp.next.num == node.num){
            //如果后一节点等于新节点,抛异常
            throw new RuntimeException("插入节点与已有节点序号重复!");
        }
        //如果后一节点比当前节点小,就继续遍历
        temp = temp.next;
    }
}

2.删除

由于相对单链表,双向链表的节点可以自己找到上一节点,所以删除的时候可以直接找到要删除的节点进行操作。

举个例子,假设有节点A,B,C,现在要删除B:

  1. 找到B,让B.prev.next=B.next,也就是让A的next指向C
  2. B.next.prev=B.prev,也就是让C的prev指向A

如果要删除的节点已经是尾节点了,那就跟单链表一样了。

/**
 * 删除节点
 * @param num 要删除的节点编号
 */
public void delete(int num) {
    Node temp = head;
    //判断链表是否为空
    if (temp.next == null) {
        throw new RuntimeException("链表为空!");
    }
    //遍历链表
    while (true) {
        //如果链表到底了
        if (temp.next == null) {
            throw new RuntimeException("编号为" + num + "的节点不存在!");
        }
        //如果找到了待删除节点的前一个节点
        if (temp.num == num) {
            //判断待删除节点是否为尾节点
            if (temp.next == null){
                temp.prev.next = null;
            }else {
                temp.prev.next = temp.next;
                temp.next.prev = temp.prev;
            }
            return;
        }
        //继续遍历下一节点
        temp = temp.next;
    }
}

3.修改,查询(与单链表一致)

由于修改和查询与单链表基本一致,这里就不在赘述了,直接放代码:

/**
 * 展示链表
 */
public void show() {
    //判断链表是否为空
    if (head.next == null) {
        throw new RuntimeException("链表为空!");
    }
    Node temp = head.next;
    //遍历链表
    while (true) {
        if (temp == null) {
            break;
        }
        System.out.println(temp.toString());
        temp = temp.next;
    }
}

/**
 * 根据序号获取节点
 * @param num 要获取的节点序号
 * @return
 */
public Node get(int num){
    //判断链表是否为空
    if (head.next == null) {
        throw new RuntimeException("链表为空!");
    }
    Node temp = head.next;
    //遍历链表
    while (true) {
        if (temp == null) {
            throw new RuntimeException("编号为" + num + "的节点不存在!");
        }
        if (temp.num == num) {
            return temp;
        }
        temp = temp.next;
    }
}

/**
 * 修改节点
 * @param node 要更新的节点
 */
public void update(Node node) {
    Node temp = head;
    //判断链表是否为空
    if (temp.next == null) {
        throw new RuntimeException("链表为空!");
    }
    //获取要更新的节点序号
    int nodeNum = node.num;
    //遍历链表
    while (true) {
        //如果已经遍历完链表
        if (temp == null) {
            throw new RuntimeException("编号为" + temp.num + "的节点不存在!");
        }
        //如果找到了该节点
        if (temp.num == nodeNum) {
            temp.data = node.data;
            return;
        }
        //继续遍历下一节点
        temp = temp.next;
    }
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/170817.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • vim中复制粘贴快捷键_复制文件的快捷键

    vim中复制粘贴快捷键_复制文件的快捷键yy复制游标所在行整行。或大写一个Y。 2yy或y2y复制两行。ㄟ,请举一反三好不好!:-) y^复制至行首,或y0。不含游标所在处字元。 y$复制至行尾。含游标所在处字元。 yw复制一个word。 y2w复制两个字(单词)。 yG复制至档尾。 y1G复制至档首。 p小写p代表贴至游标后(下)。 P大写P代表贴至游标前(上)。如果只是想使用系统粘贴板的话直接在输入模式按Shift+Inset…

    2022年9月22日
    0
  • python test suite_vue进度条插件

    python test suite_vue进度条插件前言在我们进行自动化测试的时候,用例往往是成百上千,执行的时间是几十分钟或者是小时级别。有时,我们在调试那么多用例的时候,不知道执行到什么程度了,而pytest-sugar插件能很好解决我们的痛点。

    2022年7月31日
    1
  • IOS版的MT4软件如何下载安装「建议收藏」

    IOS版的MT4软件如何下载安装「建议收藏」T4的版本总体来说还是比较全。包含了电脑版、安卓版/ios版,以及mac版。那么今天来重点讲解一下IOS版本的mt4如何下载。下载方式主要分以下2种:(1)在网页上获取安装包,通常mt4。yhtz。cc涵盖了多个版本的安装包,选择你需要的IOS即可下载到手机上后,然后就进入安装流程,点击下一步,下一步,总体来说,就是跟着安装提示走即可。(2)IOS属于手机版,IOS是可以在苹果应用商店下载的。下载流程就是:搜索Mt4,然后点击下载,一般会自动安装哦,就跟下载其他的客户端是一样的。下载安装好后

    2022年8月15日
    1
  • bookkeeper具体做什么_reading for knowledge翻译

    bookkeeper具体做什么_reading for knowledge翻译序言,关于capCAP是分布式系统中的一个特别重要的理论。CAP原则又称CAP定理,指的是在一个分布式系统中,Consistency(一致性)、Availability(可用性)、Partitiontolerance(分区容错性),三者不可得兼。CAP是NOSQL数据库的基石。分布式系统的CAP理论:理论首先把分布式系统中的三个特性进行了如下归纳:一致性(C):在分布式系统中的所…

    2022年10月28日
    0
  • vim编辑器

    vim编辑器

    2022年4月3日
    39
  • esp32显示屏_1602a液晶屏显示不了

    esp32显示屏_1602a液晶屏显示不了文章记录了本硬件小白尝试使用ESP32芯片点亮1602A屏幕

    2022年9月22日
    0

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号