数据结构与算法学习五:双链表的增、删、改、查

简介: 双链表的增、删、改、查操作及其Java实现,并通过实例演示了双向链表的优势和应用。

前言

一、双链表的应用实例

使用带head头节点的双向链表实现 - 水浒传英雄排行榜

二、单链表的缺点分析

  1. 单向链表,查找的方向只能是一个方向,而双向链表可以向前或者向后查找。
  2. 单向链表 不能自我删除 ,需要靠辅助节点 ,而双向链表,则可以自我删除,所以前面我们单链表删除时节点,总是找到temp,temp是待删除节点的前一个节点(认真体会).

三、双链表增删改查思路

  • 分析 双向链表的遍历,添加,修改,删除的操作思路
  1. 遍历 方和 单链表一样,只是可以向前,也可以向后查找
  2. 第一种添加 (默认添加到双向链表的最后)
    (1) 先找到双向链表的最后这个节点
    (2) temporary.next = newNode;
    (3) newNode.pre = temporary;
  3. 第二种添加 (按编号的顺序添加)
    (1)用辅助节点的No与新插入的No比较,找到要插入的前一个节点
    (2)先挂载后面:newNode.next = temporary.next; temporary.next.pre = newNode;
    (3)在挂载前面:temporary.next = newNode; newNode.pre = temporary;
  4. 修改 思路和 原来的单向链表一样.
  5. 删除
    (1) 因为是双向链表,因此,我们可以实现自我删除某个节点
    (2) 直接找到要删除的这个节点,比如temp
    (3) temp.pre.next = temp.next
    (4) temp.next.pre = temp.pre;
  • 上面的思路说明的逻辑图如下:
    在这里插入图片描述

四、代码实现

4.1 项目结构

HeroNode.java :节点类
DoubleLinkedList.java : 双链表类
DoubleLinkedListMain.java : 测试类
在这里插入图片描述

4.2 HeroNode.java 节点类

package com.feng.ch04_doubleLinkedlist;

// 定义HeroNode ,每个 HeroNode 就是一个节点
public class HeroNode {
    public int no;
    public String name;
    public String nickname;
    public HeroNode pre; // 指向上一个节点,默认为null
    public HeroNode next; // 指向下一个节点,默认为null

    public HeroNode(int no, String name, String nickname){
        this.no = no;
        this.name = name;
        this.nickname = nickname;
    }

    /* 为显示方法,重写 toString 方法*/
    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", nickname='" + nickname + '\'' +
                '}';
    }
}

4.3 DoubleLinkedList.java 双链表类

package com.feng.ch04_doubleLinkedlist;

/*
 * 创建一个双向链表的类
 * */
public class DoubleLinkedList {

    private HeroNode head = new HeroNode(0, "", "");

    public HeroNode getHead() {
        return head;
    }

    /*
     * 添加节点:按顺序添加
     * */
    public void addDoubleLinkedByOrder(HeroNode newNode) {
        HeroNode temporary = head;
        boolean flag = false;
        while (true) {
            if (temporary.next == null) {  // 当前数据
                break;
            }
            if (temporary.next.no > newNode.no) {
                break;
            } else if (temporary.next.no == newNode.no) {
                flag = true;
                break;
            }
            temporary = temporary.next;
        }
        if (flag) {
            System.out.printf("准备插入的英雄的编号 %d 已经存在了, 不能加入\n", newNode.no);
        } else {
            if (temporary.next == null){
                // 挂载前面
                temporary.next = newNode;
                newNode.pre = temporary;
            }else {
                //挂载后面
                newNode.next = temporary.next;
                temporary.next.pre = newNode;
                // 挂载前面
                temporary.next = newNode;
                newNode.pre = temporary;
            }
        }

    }

    /*
     * 添加节点:直接添加到双向链表的最后
     * */
    public void addDoubleLinked(HeroNode newNode) {
        HeroNode temporary = head;
        while (true) {
            if (temporary.next == null) { // 最后一个 出去
                break;
            }
            temporary = temporary.next; // 后移
        }
        /*
         * 当退出while循环时,temp就指向了链表的最后
         * 形成一个双向链表
         * */
        temporary.next = newNode;
        newNode.pre = temporary;
    }

    /*
     * 修改:可以看到双向链表的节点内容修改和单向链表一样
     * */
    public void updateNode(HeroNode newNode) {
        if (head.next == null) {
            return;
        }
        HeroNode temporary = head.next;
        boolean flag = false;
        while (true) {
            if (temporary == null) { // 没有匹配着
                break;
            }
            if (temporary.no == newNode.no) {
                flag = true;
                break;
            }
            temporary = temporary.next;
        }
        if (flag) {      // 根据flag 判断是否找到要修改的节点
            temporary.name = newNode.name;
            temporary.nickname = newNode.nickname;
        } else {
            System.out.printf("没有找到 编号 %d 的节点,不能修改\n", newNode.no);
        }
    }

    /*
     * 删除节点
     * 1 对于双向链表,我们可以直接找到要删除的这个节点
     * 2 找到后,自我删除即可
     * */
    public void deleteNode(int no) {
        if (head.next == null) {
            System.out.println("链表为空,无法删除");
            return;
        }
        HeroNode temporary = head.next;
        boolean flag = false;
        while (true) {
            if (temporary == null) {
                break;
            }
            if (temporary.no == no) {
                flag = true;
                break;
            }
            temporary = temporary.next;
        }
        if (flag) {
            temporary.pre.next = temporary.next;
            if (temporary != null) {
                temporary.next.pre = temporary.pre;
            }
        } else {
            System.out.printf("要删除的 %d 节点不存在\n", no);
        }
    }

    //显示链表【遍历】
    public void list() {
        if (head.next == null) {
            System.out.println("链表为空");
            return;
        }
        // 因为头结点不能动, 因此需要一个辅助节点 temp 来遍历
        HeroNode temporary = head.next;
        while (temporary != null) {
            // 输出节点信息
            System.out.println(temporary);
            temporary = temporary.next;
        }
    }
}

4.4 DoubleLinkedListMain.java 测试类

package com.feng.ch04_doubleLinkedlist;

public class DoubleLinkedListMain {
    public static void main(String[] args) {
        DoubleLinkedList doubleLinkedList = new DoubleLinkedList();

        HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
        HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
        HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
        HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");

//        doubleLinkedList.addDoubleLinked(hero1);
//        doubleLinkedList.addDoubleLinked(hero2);
//        doubleLinkedList.addDoubleLinked(hero3);
//        doubleLinkedList.addDoubleLinked(hero4);

        doubleLinkedList.addDoubleLinkedByOrder(hero2);
        doubleLinkedList.addDoubleLinkedByOrder(hero1);
        doubleLinkedList.addDoubleLinkedByOrder(hero4);
        doubleLinkedList.addDoubleLinkedByOrder(hero3);

        System.out.println("初始化的 双链表数据:");
        doubleLinkedList.list();
        // 测试修改
        System.out.println();
        System.out.println("修改后的数据~");
        HeroNode hero5 = new HeroNode(4, "公孙胜", "入云龙");
        doubleLinkedList.updateNode(hero5);
        doubleLinkedList.list();

        // 测试删除
        System.out.println();
        System.out.println("删除后的数据~");
        doubleLinkedList.deleteNode(3);
        doubleLinkedList.list();
    }
}

4.5 测试结果

在这里插入图片描述

相关文章
|
2天前
|
存储 缓存 关系型数据库
MySQL事务日志-Redo Log工作原理分析
事务的隔离性和原子性分别通过锁和事务日志实现,而持久性则依赖于事务日志中的`Redo Log`。在MySQL中,`Redo Log`确保已提交事务的数据能持久保存,即使系统崩溃也能通过重做日志恢复数据。其工作原理是记录数据在内存中的更改,待事务提交时写入磁盘。此外,`Redo Log`采用简单的物理日志格式和高效的顺序IO,确保快速提交。通过不同的落盘策略,可在性能和安全性之间做出权衡。
1517 4
|
29天前
|
弹性计算 人工智能 架构师
阿里云携手Altair共拓云上工业仿真新机遇
2024年9月12日,「2024 Altair 技术大会杭州站」成功召开,阿里云弹性计算产品运营与生态负责人何川,与Altair中国技术总监赵阳在会上联合发布了最新的“云上CAE一体机”。
阿里云携手Altair共拓云上工业仿真新机遇
|
5天前
|
人工智能 Rust Java
10月更文挑战赛火热启动,坚持热爱坚持创作!
开发者社区10月更文挑战,寻找热爱技术内容创作的你,欢迎来创作!
492 19
|
2天前
|
存储 SQL 关系型数据库
彻底搞懂InnoDB的MVCC多版本并发控制
本文详细介绍了InnoDB存储引擎中的两种并发控制方法:MVCC(多版本并发控制)和LBCC(基于锁的并发控制)。MVCC通过记录版本信息和使用快照读取机制,实现了高并发下的读写操作,而LBCC则通过加锁机制控制并发访问。文章深入探讨了MVCC的工作原理,包括插入、删除、修改流程及查询过程中的快照读取机制。通过多个案例演示了不同隔离级别下MVCC的具体表现,并解释了事务ID的分配和管理方式。最后,对比了四种隔离级别的性能特点,帮助读者理解如何根据具体需求选择合适的隔离级别以优化数据库性能。
179 1
|
8天前
|
JSON 自然语言处理 数据管理
阿里云百炼产品月刊【2024年9月】
阿里云百炼产品月刊【2024年9月】,涵盖本月产品和功能发布、活动,应用实践等内容,帮助您快速了解阿里云百炼产品的最新动态。
阿里云百炼产品月刊【2024年9月】
|
21天前
|
存储 关系型数据库 分布式数据库
GraphRAG:基于PolarDB+通义千问+LangChain的知识图谱+大模型最佳实践
本文介绍了如何使用PolarDB、通义千问和LangChain搭建GraphRAG系统,结合知识图谱和向量检索提升问答质量。通过实例展示了单独使用向量检索和图检索的局限性,并通过图+向量联合搜索增强了问答准确性。PolarDB支持AGE图引擎和pgvector插件,实现图数据和向量数据的统一存储与检索,提升了RAG系统的性能和效果。
|
8天前
|
Linux 虚拟化 开发者
一键将CentOs的yum源更换为国内阿里yum源
一键将CentOs的yum源更换为国内阿里yum源
448 5
|
7天前
|
存储 人工智能 搜索推荐
数据治理,是时候打破刻板印象了
瓴羊智能数据建设与治理产品Datapin全面升级,可演进扩展的数据架构体系为企业数据治理预留发展空间,推出敏捷版用以解决企业数据量不大但需构建数据的场景问题,基于大模型打造的DataAgent更是为企业用好数据资产提供了便利。
313 2
|
23天前
|
人工智能 IDE 程序员
期盼已久!通义灵码 AI 程序员开启邀测,全流程开发仅用几分钟
在云栖大会上,阿里云云原生应用平台负责人丁宇宣布,「通义灵码」完成全面升级,并正式发布 AI 程序员。
|
25天前
|
机器学习/深度学习 算法 大数据
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
2024“华为杯”数学建模竞赛,对ABCDEF每个题进行详细的分析,涵盖风电场功率优化、WLAN网络吞吐量、磁性元件损耗建模、地理环境问题、高速公路应急车道启用和X射线脉冲星建模等多领域问题,解析了问题类型、专业和技能的需要。
2608 22
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析