Java数据结构之双向链表(配图详解,简单易懂)

简介: 本笔记针对无头双向链表的实现来展开,在阅读该笔记时,建议读者结合博主的单链表笔记一起阅读,两者多有相似之处,结合阅读便于理解总结

双向链表


双向链表结构其实与单向链表结构非常相似,只是比单向链表多了prev域用于存储前一个节点的地址,从而实现链表的双向性,见下图


微信图片_20230110153604.png

节点类及链表头尾的建立


class Node {
    public int data;//一个节点存在三个区域
    public Node prev;
    public Node next;
    public Node(int data) {//构造方法用于初始化实例对象
        this.data = data;
    }
}
public class MyLinkedList {
    public Node head;
    public Node tail;
    public void addFirst(int data);
    //1.头插法
    public void addLast(int data);
    //2.尾插法
    public void display();
    //3.打印链表
    public boolean contains(int key);
    //4.查找是否包含关键字key是否在单链表当中 
    public int size();
    //5.求链表长度
    public void addIndex(int index,int data);
    //6.任意位置插入,第一个数据节点为0号下标 
    public void remove(int key);
    //7.删除第一次出现关键字为key的节点 
    public void removeAllKey(int key);
    //8.删除所有值为key的节点 
    public void clear();
    //9.清空链表
}


以下即为双向链表的各接口的实现


1.头插法


头插节点有两种情况,第一种情况时空链表第一次插入,第二种为后续插入


微信图片_20230110153613.png

public void addFirst(int data) {
        Node node=new Node(data);//将data的值new为一个节点
        if (this.head == null) {//第一次插入
            this.head=node;//头尾节点都指向该节点
            this.tail=node;
        }else {
            node.next=this.head;//完成连接
            this.head.prev=node;//改变域值
            this.head=node;//头节点前移
        }
    }


2.尾插法

与头插类似,两种情况>


微信图片_20230110153616.png


 

public void addLast(int data) {
        Node node=new Node(data);
        if(this.head==null) {
            this.head=node;
            this.tail=node;
        }else {
            this.tail.next=node;//续尾
            node.prev=this.tail;//变值
            this.tail=node;//移尾
        }
    }


3.打印链表


遍历链表完成打印


 

public void display() {
        Node cur=this.head;
        while(cur != null) {
            System.out.print(cur.data+" ");
            cur=cur.next;
        }
        System.out.println();
    }


4.查找是否包含关键字


遍历链表,查找是否包含关键字


 

public boolean contains(int key) {
        Node cur=this.head;
        while(cur!=null) {
            if(cur.data==key)
                return true;
            cur=cur.next;
        }
        return false;
    }


5.求链表长度


遍历求和


 

public int size() {
        int a=0;
        Node cur=this.head;
        while(cur!=null) {
            a++;
            cur=cur.next;
        }
        return a;
    }


6.任意位置(index)插入


完成这项任务需要分步进行


1.判断index的合法性

2.是否为头插

3.是否为尾插

4.中间插入


微信图片_20230110153621.png


在单链表删除关键字时,还需要设置前后节点完成连接,因为双向链表中存在上一个节点的地址,所以只需要设置一个节点遍历即可完成任务


 

private void checkIndex(int index) {//判断index位置合法性
        if(index<0||index>this.size()) {
            throw new RuntimeException("index位置不合法");
        }
    }
    private Node searchIndex(int index) {//查找插入的位置
        Node cur=this.head;
        int a=0;
        while(a!=index) {
            a++;
            cur=cur.next;
        }
        return cur;
    }
    public void addIndex(int index,int data) {
        checkIndex(index);
        if(index==0) {//头插
            this.addFirst(data);
            return;
        }
        if(index==this.size()) {//尾插
            this.addLast(data);
            return;
        }
        Node node=new Node(data);//实例化node节点
        Node cur=searchIndex(index);//cur存储index位置节点
        node.next=cur;//左边四步完成连接过程,先对node中的值改变对原链表无影响
        node.prev=cur.prev;
        cur.prev.next=node;//然后连接前后
        cur.prev=node;
    }


7.删除第一次出现关键字为key的节点


设置cur节点遍历链表,当cur.data==key时,删除该节点(特殊极端情况单独考虑)>

微信图片_20230110153624.png


 

public void remove(int key) {
        Node cur=this.head;
        while(cur!=null) {
            if(cur.data==key) {//如果找到关键字key
                if(cur==this.head) {//头节点的data为key
                    this.head=cur.next;//头节点后移完成头节点删除
                    if(this.head!=null)//防止空指针异常
                    this.head.prev=null;
                }else {//中间找到key
                    cur.prev.next=cur.next;
                    if(cur.next!=null)
                    cur.next.prev=cur.prev;
                    else//如果cur.next==null,尾节点即为所需删除节点
                        this.tail=cur.prev;
                }
                break;//完成删除后跳出循环
            }
            cur=cur.next;//如果没有进if语句中,cur继续往后遍历
        }
    }


8.删除所有值为key的节点


与7代码相同,只是无需跳出循环,让cur完成遍历,把关键字全部删除即可


public void remove(int key) {
        Node cur=this.head;
        while(cur!=null) {
            if(cur.data==key) {//如果找到关键字key
                if(cur==this.head) {//头节点的data为key
                    this.head=cur.next;//头节点后移完成头节点删除
                    if(this.head!=null)//防止空指针异常
                    this.head.prev=null;
                }else {//中间找到key
                    cur.prev.next=cur.next;
                    if(cur.next!=null)
                    cur.next.prev=cur.prev;
                    else//如果cur.next==null,尾节点即为所需删除节点
                        this.tail=cur.prev;
                }
            }
            cur=cur.next;//如果没有进if语句中,cur继续往后遍历
        }
    }

9.清空链表


在单链表清空链表时,直接将this.head置为空即可(当this.head置为空时,没有对象引用头节点,则其内存被JVM自动收回,后续节点也被收会),而当双向链表清空时,只把this.head置为空时,因为为双向链表,所以第二个节点的prev仍然指向头节点,所以无法完成清空,则需遍历链表完成置空.


 

public void clear() {//完成遍历,所有都置为空,则内存被收回
        while (this.head!=null) {
            Node cur=this.head.next;
            this.head.prev=null;
            this.head.next=null;
            this.head=cur;
        }
        this.tail=null;
    }
相关文章
|
24天前
|
存储 Java
Java中的HashMap和TreeMap,通过具体示例展示了它们在处理复杂数据结构问题时的应用。
【10月更文挑战第19天】本文详细介绍了Java中的HashMap和TreeMap,通过具体示例展示了它们在处理复杂数据结构问题时的应用。HashMap以其高效的插入、查找和删除操作著称,而TreeMap则擅长于保持元素的自然排序或自定义排序,两者各具优势,适用于不同的开发场景。
41 1
|
26天前
|
存储 Java
告别混乱!用Java Map优雅管理你的数据结构
【10月更文挑战第17天】在软件开发中,随着项目复杂度增加,数据结构的组织和管理至关重要。Java中的Map接口提供了一种优雅的解决方案,帮助我们高效、清晰地管理数据。本文通过在线购物平台的案例,展示了Map在商品管理、用户管理和订单管理中的具体应用,有效提升了代码质量和维护性。
80 2
|
26天前
|
存储 Java 开发者
Java Map实战:用HashMap和TreeMap轻松解决复杂数据结构问题!
【10月更文挑战第17天】本文深入探讨了Java中HashMap和TreeMap两种Map类型的特性和应用场景。HashMap基于哈希表实现,支持高效的数据操作且允许键值为null;TreeMap基于红黑树实现,支持自然排序或自定义排序,确保元素有序。文章通过具体示例展示了两者的实战应用,帮助开发者根据实际需求选择合适的数据结构,提高开发效率。
57 2
|
9天前
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
30 6
|
15天前
|
存储 Java 索引
Java中的数据结构:ArrayList和LinkedList的比较
【10月更文挑战第28天】在Java编程世界中,数据结构是构建复杂程序的基石。本文将深入探讨两种常用的数据结构:ArrayList和LinkedList,通过直观的比喻和实例分析,揭示它们各自的优势与局限,帮助你在面对不同的编程挑战时做出明智的选择。
|
17天前
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
44 4
|
18天前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之单双链表精题详解(9)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
18天前
|
存储 Web App开发 算法
2024重生之回溯数据结构与算法系列学习之单双链表【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构之单双链表按位、值查找;[前后]插入;删除指定节点;求表长、静态链表等代码及具体思路详解步骤;举例说明、注意点及常见报错问题所对应的解决方法
|
23天前
|
存储 算法 Java
Java 中常用的数据结构
【10月更文挑战第20天】这些数据结构在 Java 编程中都有着广泛的应用,掌握它们的特点和用法对于提高编程能力和解决实际问题非常重要。
24 6
|
24天前
|
存储 Java 开发者
Java中的Map接口提供了一种优雅的方式来管理数据结构,使代码更加清晰、高效
【10月更文挑战第19天】在软件开发中,随着项目复杂度的增加,数据结构的组织和管理变得至关重要。Java中的Map接口提供了一种优雅的方式来管理数据结构,使代码更加清晰、高效。本文通过在线购物平台的案例,展示了Map在商品管理、用户管理和订单管理中的具体应用,帮助开发者告别混乱,提升代码质量。
26 1