【JavaSE】双向链表的实现与讲解

上次文章我们实现了单向链表,链接如下 : 单向链表的实现与讲解
而这篇文章博主就对双向链表如何实现以及实现原理进行讲解;
双向链表和单向链表本质上区别不大,双向链表就比单向链表每个节点多了一个先驱prev,以实现链表能够逆向链接:
在这里插入图片描述
双向链表需要有一个头结点引用head 以及一个尾结点引用last
而有力单向链表的知识基础,双向链表实现以及理解上就会容易很多,讲解如下 :

1.节点类实现

class ListNode{
    public int val; //数据域
    public ListNode prev;//前驱域
    public ListNode next;//后驱域

    public ListNode(int val){ //一个参数的构造方法 实现对数据域的数据赋值
        this.val=val; //this : 表示当前对象引用(注意不是当前对象)
    }
}

2. 双向链表各个模块功能实现

2.1 头插法

//头插法
    public void addFirst(int data) {
        ListNode node = new ListNode(data); //实例化插入的节点
        if (this.head == null) {//链表为空情况
            this.head = node;
            this.last = node;
        } else { 
            node.next = this.head; //node的next改为head
            this.head.prev = node; //head的prev改为node
            this.head = node; //此时新的head为node
        }
    }

在这里插入图片描述

2.2 尾插法

//尾插法
    public void addLast(int data) {
        ListNode node = new ListNode(data);//实例化插入的节点
        if (head == null) { //链表为空情况
            this.head = node;
            this.last = node;
        } else {
            this.last.next = node; //last尾结点的next改为node 
            node.prev = this.last; //node的前驱prev改为last
            this.last = node; //此时last为node 
        }
    }

在这里插入图片描述

2.3 查找关键字key是否在单链表当中

 //查找是否包含关键字key是否在单链表当中
    public boolean contains(int key){
        ListNode cur=this.head; //定义cur引用指向head
        while (cur!=null){ //遍历链表
            if (cur.val==key){
                return true; //找到返回TRUE
            }
            cur=cur.next; //没找到继续找下一个节点
        }
        return false; //遍历完成没有返回true 则没有找到 返回false 
    }

在这里插入图片描述

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

单向链表删除节点操作要先找到需要删除的那个节点的前一个节点,在进行删除操作;而双向链表不需要找前一个节点,找到要删除的节点它本身就可以进行操作


    //删除第一次出现关键字为key的节点
    public void remove(int key){
        ListNode cur=this.head; //cur引用来遍历
        while(cur!=null){ //遍历
            if (cur.val==key){ 
                if (cur==head){ //要删除的节点是头节点情况
                    head=head.next; //head直接引用向下一个节点
                    if(head!=null{
                    head.prev=null; //下一个节点的prev改为null
                    }else{
                    last=null//因为last是成员变量 不手动制空会一直浪费着内存
                    }
                }else if(cur==last){ //删除节点是尾结点情况
                    cur.prev.next=cur.next; //尾结点的前一个节点的next改为此时尾结点的next 也就是null
                    last=last.prev; //last引用向前一个节点 实现删除
                }else{ //节点既不是头结点也不是尾结点的情况
                    cur.prev.next=cur.next; //前一个节点的next直接指向后一个节点 
                    cur.next.prev=cur.prev; //后一个节点的prev直接指向前一个节点  实现删除
                }
                return; //只删除一个 删除执行的话返回 
            }else{
                cur=cur.next; //没有找到的话  继续查找下一个节点
            }
        }
    }

在这里插入图片描述

2.5 删除所有值为key的节点

步骤与删除一次key相同 只是在删除操作后不return 继续进行遍历删除

 //删除所有值为key的节点
    public void removeAllKey(int key){
        ListNode cur=this.head;
        while(cur!=null){
            if (cur.val==key){
                if (cur==head){
                    head=head.next;
                    if(head!=null){
                        head.prev=null;
                    }else{
                        last=null;
                    }
                }else if(cur==last){
                    cur.prev.next=cur.next;
                    last=last.prev;
                }else{
                    cur.prev.next=cur.next;
                    cur.next.prev=cur.prev;
                }
                // return; 删除一次是直接return 删除所有的话就不return 继续进行遍历删除
            }else{
                cur=cur.next;
            }
        }

    }

2.6 得到单链表的长度

//得到单链表的长度
    public int size(){
        ListNode cur=this.head; //cur引用来遍历
        int count=0; //计数器
        while (cur!=null){ //遍历操作
            count++; 
            cur=cur.next; 
        }
        return count; //返回长度 
    }

2.7 打印单链表

 public void display(){
        ListNode cur=this.head;//cur引用来遍历
        while(cur!=null){//遍历 
            System.out.print(cur.val+" ");
            cur=cur.next;
        }
        System.out.println(); 
    }

2.8任意位置插入,第一个数据节点为0号下标

回顾上次的单向链表,我们要在index位置插入需要找到index位置前面一个节点,而这里的双向链表 我们要在index插入 就只需要找到index位置的节点进行操作
首先我们要完成一个查找index位置节点的方法

 public ListNode searchIndex(int index){
       ListNode cur=this.head; 
       while (index!=0){ 
           cur=cur.next;
           index--;
       }
       return cur; //找到index位置的节点进行返回
     }

进行插入操作 :

  //任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index,int data){
       ListNode node=new ListNode(data); //实例化需要插入的节点
       if (index<0 || index>size()){ //判断index是否合法
           System.out.println("index位置不合法!");
           return;
       }
       if (index==0){ //头插法 
           addFirst(data);
           return;
       }
       if (index==size()){// 尾插法
           addLast(data);
           return;
       }
       ListNode cur=searchIndex(index); //找到index位置节点定义为cur
       // 进行插入操作 :插入操作需要改变**4个**引用域的值 实现插入 
       node.next=cur.prev.next; 
       cur.prev.next=node;
       node.prev=cur.prev;
       cur.prev=node;
    }

在这里插入图片描述
需要改变的4个引用域就是蓝色圈起来的4个引用域 具体改变代码 :

node.next=cur.prev.next; 
       cur.prev.next=node;
       node.prev=cur.prev;
       cur.prev=node;

照着进行画图就能很容易理解 ~

2.9 清空链表

public void clear(){
//不采用直接把head last制空的暴力情况方法 
//而是采取逐一情况prev last的操作
        ListNode cur=this.head;
        while(cur!=null){
            ListNode curNext=cur.next;
            cur.prev=null;
            cur.next=null;
            cur=curNext;
        }
        //不要忘记把head last 引用也制空
        this.head=null;
        this.last=null;
    }

在这里插入图片描述
清空成功 !

3. 整体 MyLinkedList 代码展示

class ListNode{
    public int val;
    public ListNode prev;
    public ListNode next;

    public ListNode(int val){
        this.val=val;
    }
}

public class MyLinkedList {
    ListNode head;
    ListNode last;

    // 2、无头双向链表实现

    //头插法
    public void addFirst(int data) {
        ListNode node = new ListNode(data);
        if (this.head == null) {
            this.head = node;
            this.last = node;
        } else {
            node.next = this.head;
            this.head.prev = node;
            this.head = node;
        }
    }

    //尾插法
    public void addLast(int data) {
        ListNode node = new ListNode(data);
        if (head == null) {
            this.head = node;
            this.last = node;
        } else {
            this.last.next = node;
            node.prev = this.last;
            this.last = node;
        }
    }

     public ListNode searchIndex(int index){
       ListNode cur=this.head;
       while (index!=0){
           cur=cur.next;
           index--;
       }
       return cur;
     }


    //任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index,int data){
       ListNode node=new ListNode(data);
       if (index<0 || index>size()){
           System.out.println("index位置不合法!");
           return;
       }
       if (index==0){
           addFirst(data);
           return;
       }
       if (index==size()){
           addLast(data);
           return;
       }
       ListNode cur=searchIndex(index);
       node.next=cur.prev.next;
       cur.prev.next=node;
       node.prev=cur.prev;
       cur.prev=node;
    }

    //查找是否包含关键字key是否在单链表当中
    public boolean contains(int key){
        ListNode cur=this.head;
        while (cur!=null){
            if (cur.val==key){
                return true;
            }
            cur=cur.next;
        }
        return false;
    }


    //删除第一次出现关键字为key的节点
    public void remove(int key){
        ListNode cur=this.head;
        while(cur!=null){
            if (cur.val==key){
                if (cur==head){
                    head=head.next;
                    if(head!=null){
                        head.prev=null;
                    }else{
                        last=null;
                    }
                }else if(cur==last){
                    cur.prev.next=cur.next;
                    last=last.prev;
                }else{
                    cur.prev.next=cur.next;
                    cur.next.prev=cur.prev;
                }
                return;
            }else{
                cur=cur.next;
            }
        }
    }


    //删除所有值为key的节点
    public void removeAllKey(int key){
        ListNode cur=this.head;
        while(cur!=null){
            if (cur.val==key){
                if (cur==head){
                    head=head.next;
                    if(head!=null){
                        head.prev=null;
                    }else{
                        last=null;
                    }
                }else if(cur==last){
                    cur.prev.next=cur.next;
                    last=last.prev;
                }else{
                    cur.prev.next=cur.next;
                    cur.next.prev=cur.prev;
                }
                // return;
            }else{
                cur=cur.next;
            }
        }

    }
    //得到单链表的长度
    public int size(){
        ListNode cur=this.head;
        int count=0;
        while (cur!=null){
            count++;
            cur=cur.next;
        }
        return count;
    }

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

    public void clear(){
        ListNode cur=this.head;
        while(cur!=null){
            ListNode curNext=cur.next;
            cur.prev=null;
            cur.next=null;
            cur=curNext;
        }
        this.head=null;
        this.last=null;
    }

}


4. 部分测试 test 代码展示 :

public class TestDemo {
    public static void main(String[] args) {
        MyLinkedList myLinkedList=new MyLinkedList();
        myLinkedList.addFirst(1);
        myLinkedList.addFirst(2);
        myLinkedList.addFirst(3);
       // System.out.println(myLinkedList.size());
        myLinkedList.display();
      //  System.out.println(myLinkedList.contains(2));
      //  myLinkedList.remove(3);
      //  myLinkedList.display();
        System.out.println("-------------------");

        myLinkedList.clear();
        myLinkedList.display();

    }

}

在这里插入图片描述

5. 总结

讲解到这里 双向链表的实现就已经完成了
对于链表的学习我们一定要多画图理解 多思考 多写代码 才能真正学好链表

如果觉得博主的文章对自己有所帮助 欢迎大家多多点赞收藏 ~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值