1 双向链表

1.1 双向链表介绍

相较单链表,双向链表除了data与next域,还多了一个pre域用于表示每个节点的前一个元素。这样做给双向链表带来了很多优势:

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

单链表如果想要实现删除操作,需要找到待删除节点的前一个节点。而双向链表可以实现自我删除。

双向链表结构示意图如下:

1.2 双向链表实现思路

与单链表实现类似,交集部分不再赘述,详情可参考文章:Java数据结构:单链表的实现与面试题汇总

遍历:

与单链表遍历方式一样,同时,双向链表可以支持向前和向后两种查找方式

添加:

添加到末尾

  • 先找到双向链表最后一个节点
  • cur.next = newNode;
  • newNode.pre = cur;

按照no顺序添加

  • 先判断该链表是否为空,如果为空则直接添加
  • 如果不为空则继续处理
  • 根据no遍历链表,找到合适的位置
  • newNode.next = cur.next;
  • cur.next = newNode;
  • newNode.pre = cur;

修改操作与单链表实现步骤一致

删除操作:

  • 双向链表可以实现自我删除
  • 直接找到待删除的节点
  • cur.pre.next = cur.next;
  • cur.next.pre = cur.pre;
  • 需要特别注意是否为最后一个元素,如果为最后一个元素,cur.next为null,此时使用cur.next.pre会出现空指针异常,所以,如果为最后一个元素,则该步骤可以省略,cur.next为null即可。

以删除红色的2号节点为例:

2 双向链表实现完整代码

2.1 节点类 Student.java

/**
 * @author 兴趣使然黄小黄
 * @version 1.0
 * 学生类节点
 */
public class StudentNode {
    public String no; //学号
    public String name; //姓名
    public int age; //年龄
    public StudentNode pre; //指向前一个节点
    public StudentNode next; //指向下一个节点

    //构造器
    public StudentNode(String no, String name, int age ){
        this.no = no;
        this.name = name;
        this.age = age;
    }

    //为了显示方便
    @Override
    public String toString() {
        return "StudentNode{"  
                "no='"   no   '\''  
                ", name='"   name   '\''  
                ", age="   age  
                '}';
    }
}

2.2 双向链表实现类 StudentDoubleLinkedList.java

/**
 * @author 兴趣使然黄小黄
 * @version 1.0
 * 学生双向链表的具体实现
 */
public class StudentDoubleLinkedList {
    //定义头节点
    private StudentNode head = new StudentNode("", "", 0);

    //获取头节点
    public StudentNode getHead(){
        return head;
    }

    //遍历双向链表的方法
    public void showList(){
        //判断链表是否为空
        if (head.next == null){
            System.out.println("当前链表为空");
            return;
        }
        //遍历 使用辅助指针
        StudentNode temp = head;
        while (temp != null){
            //更新指针
            temp = temp.next;
            if (temp.next == null){
                System.out.print(temp);
                break;
            }
            System.out.print(temp   "--->");
        }
    }

    //添加节点的方法
    //添加到尾部
    public void add(StudentNode newNode){
        //先找到最后一个节点
        StudentNode cur = head;
        while (true){
            //到达最后退出循环
            if (cur.next == null){
                break;
            }
            //更新指针
            cur = cur.next;
        }
        //添加操作
        newNode.next = cur.next; //也可以省略,因为恒为null
        cur.next = newNode;
        newNode.pre = cur;
    }

    //添加节点的方法
    //根据学号顺序添加
    public void addByOrder(StudentNode newNode){
        //如果当前链表为空,则直接添加
        if (head.next == null){
            head.next = newNode;
            newNode.pre = head;
            return;
        }
        //按照学号找到对应位置
        StudentNode cur = head;
        boolean flag = false; //标识待添加的no是否存在
        while (true){
            if (cur.next == null){
                //已经到达链表的尾部
                break;
            } else if (Integer.parseInt(cur.next.no) == Integer.parseInt(newNode.no)){
                //已经存在
                flag = true;
                break;
            }else if (Integer.parseInt(cur.next.no) > Integer.parseInt(newNode.no)){
                //位置找到
                break;
            }
            cur = cur.next;
        }
        if (flag){
            System.out.println("当前no已经存在,无法添加!");
        }else {
            //添加操作
            newNode.next = cur.next;
            cur.next = newNode;
            newNode.pre = cur;
        }
    }

    //根据no学号修改学生信息
    public void update(StudentNode studentNode){
        if (head.next == null){
            System.out.println("当前链表为空, 无法修改");
            return;
        }
        StudentNode temp = head.next;
        boolean flag = false; //表示是否找到节点
        while (true){
            if (temp == null){
                break;
            }
            if (temp.no == studentNode.no){
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if (flag){
            temp.name = studentNode.name;
            temp.age = studentNode.age;
            System.out.println("更新成功!");
        }else {
            System.out.println("没有找到");
        }
    }

    //从双向链表中删除节点
    public void delete(String no){
        //直接找到对应no的节点直接删除
        StudentNode cur = head.next;
        boolean flag = false; //标记是否找到
        while (true){
            if (cur == null){
                break;
            }
            if (cur.no.equals(no)){
                flag = true; //找到了
                break;
            }
            cur = cur.next;
        }
        if (flag){
            //删除操作
            //注意考虑最后一个节点后一个为空的情况
            if (cur.next != null) {
                cur.next.pre = cur.pre;
            }
            cur.pre.next = cur.next;
            System.out.println("删除成功!");
        }else {
            System.out.println( no   "节点不存在,无法删除!");
        }
    }
}

2.3 测试类 StudentDoubleLinkedListDemo.java

/**
 * @author 兴趣使然黄小黄
 * @version 1.0
 * 双向链表测试类
 */
public class DoubleLinkedListDemo {
    public static void main(String[] args) {
        StudentDoubleLinkedList doubleLinkedList = new StudentDoubleLinkedList();
        doubleLinkedList.addByOrder(new StudentNode("1", "黄小黄", 20));
        doubleLinkedList.addByOrder(new StudentNode("3", "懒羊羊", 20));
        doubleLinkedList.addByOrder(new StudentNode("2", "沸羊羊", 25));
        doubleLinkedList.addByOrder(new StudentNode("4", "美羊羊", 15));
        System.out.println("遍历:");
        doubleLinkedList.showList();

        //测试更新方法
        System.out.println("\n更新编号为4的信息后:");
        doubleLinkedList.update(new StudentNode("4", "祢豆子", 14));
        doubleLinkedList.showList();

        //测试删除方法
        System.out.println("\n删除第一个和最后一个:");
        doubleLinkedList.delete("1");
        doubleLinkedList.delete("4");
        doubleLinkedList.showList();
    }
}

2.4 结果

遍历:
StudentNode{no='1', name='黄小黄', age=20}--->StudentNode{no='2', name='沸羊羊', age=25}--->StudentNode{no='3', name='懒羊羊', age=20}--->StudentNode{no='4', name='美羊羊', age=15}
更新编号为4的信息后:
更新成功!
StudentNode{no='1', name='黄小黄', age=20}--->StudentNode{no='2', name='沸羊羊', age=25}--->StudentNode{no='3', name='懒羊羊', age=20}--->StudentNode{no='4', name='祢豆子', age=14}
删除第一个和最后一个:
删除成功!
删除成功!
StudentNode{no='2', name='沸羊羊', age=25}--->StudentNode{no='3', name='懒羊羊', age=20}
Process finished with exit code 0

3 双向链表小结

单向链表查找的方向只能为一个方向,双向链表解决了这个缺点,可以实现双向查找;

单链表进行删除操作必须找到待删除元素的前一个元素,才能完成删除操作。而双向链表就简单多了,只需要找到待删除的节点,进行自我删除;

本节介绍了双向链表的遍历、添加、按顺序添加、更新、删除方法的实现,可以尝试像单链表篇一样,尝试求有效节点数量,以及如何逆序输出双向链表加强练习!

以上就是Java数据结构之双向链表的实现的详细内容,更多关于Java数据结构双向链表的资料请关注Devmax其它相关文章!

Java数据结构之双向链表的实现的更多相关文章

  1. swift篇第一期:简单的数据结构

    首先我们可以去使用Playground来编码,并且会实时的显示对应的编码信息,这样我们就不用每次都去运行程序来显示输出的东西了哦,也方便了我们对某些语句的验证,这个是比较赞的var与let前者为可变修饰符,后者为不可变从字面意思我们就可以很好的区分了常用的类型呢,跟其他语言基本相同啦,主要有几种:1.int类型2.Float,Double类型3.String类型4.Boolean类型当我们去声明一

  2. Swift 集合数据结构性能分析

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  3. Swift中的集合类数据结构

    在那种情况下,你将会需要一种基本的集合类数据结构。继续学习,你将会比较成熟的Cocoa数据结构与对应的纯Swift结构的性能。常见iOS数据结构iOS中三种最常用的数据结构是arrays,dictionaries和sets。除了在Swift和Objective-C中旧的Foundation框架中的数据结构,现在又有了新的仅支持Swift版本的数据结构与语言紧密结合在一起。Swift数组是同质的,意味着每一个Swift数组都只包含一种类型的对象。

  4. 11.Swift 中的类和结构体

    举例来说,以下情境中适合使用结构体:1.几何形状的大小,封装一个width属性和height属性,两者均为Double类型。这次就讲到这里,下次我们继续

  5. a place you can learn algorithms and data structures(算法和数据结构) in swift

    https://github.com/raywenderlich/swift-algorithm-club

  6. Swift3.0 类和结构体的选择

    结构体实例总是通过值传递,类实例总是通过引用传递先说说值类型和引用类型的区别值类型被赋予给一个变量、常量或者被传递给一个函数的时候,其值会被拷贝在Swift中,所有的结构体和枚举类型都是值类型。实际中,这意味着绝大部分的自定义数据构造都应该是类,而非结构体”Swift中,许多基本类型,诸如String,Array和Dictionary类型均以结构体的形式实现。Objective-C中Nsstring,NSArray和NSDictionary类型均以类的形式实现,而并非结构体。

  7. 【Swift】结构体和类

    Swift中结构体和类有很多共同点与结构体相比,类还有如下的附加功能:结构体和枚举是值类型值类型被赋予给一个变量、常量或者被传递给一个函数的时候,其值会被拷贝。为了达到这个目的,Swift内建了两个恒等运算符:类和结构体的选择在你的代码中,你可以使用类和结构体来定义你的自定义数据类型。实际中,这意味着绝大部分的自定义数据构造都应该是类,而非结构体。Swift中,许多基本类型,诸如String,Array和Dictionary类型均以结构体的形式实现。

  8. 如何在Swift中创建打包数据结构?

    我正在将一个项目从Objective-C转换为Swift,我正在使用一个打包的结构来输入通过套接字发送的转换二进制消息:我不确定Swift中最好的方法是什么,我能得到的最接近的近似值是:翻译中丢失了两个重要的细节:没有保证整数类型的比特,并且没有结构打包.我不认为这可以在Swift中表达,但如果是这样,怎么样?

  9. android – 如何正确删除保留的实例片段

    解决方法正如@Luksprog所建议的,以下方法有效.但是,它仍然无法解释为什么通过onDetach完成的先前清理不起作用.如果有人能解释为什么这个解决方案有效,而以前没有,我会非常感激.版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  10. android – 数组适配器notifyDataSetChanged()将无法正常工作

    .有任何想法吗?

随机推荐

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  4. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  7. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  8. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  9. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部