Java双向链表的操作

 更新时间:2022年06月22日 16:16:31   作者:萝诗粉  
这篇文章主要介绍了Java双向链表的操作,双向链表,对于该链表中的任意节点,既可以通过该节点向前遍历,也可以通过该节点向后遍历,双向链表在实际工程中应用非常广泛,是使用链表这个结构的首选

前言

我们之前学的单链表,默认只能从链表的头部遍历到链表的尾部,在实际中应用太少见,太局限;而双向链表,对于该链表中的任意节点,既可以通过该节点向前遍历,也可以通过该节点向后遍历,双向链表在实际工程中应用非常广泛,是使用链表这个结构的首选。

一、认识双向链表

单向链表不仅保存了当前的结点值,还保存了下一个结点的地址

在这里插入图片描述

双向链表不仅保存了当前节点的值,还保存了上一个结点的地址和下一个结点的地址

在这里插入图片描述

定义一个双向链表的结点类:

结点中既要保存当前节点的值,还要保存此节点的前驱节点的地址和此节点的后继节点的地址

class DoubleNode{
    public DoubleNode next;
    DoubleNode prev;
    int val;
    DoubleNode tail;

    public DoubleNode() {}

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

    public DoubleNode(DoubleNode prev, int val, DoubleNode tail) {
        this.prev = prev;
        this.val = val;
        this.tail = tail;
    }
}

定义一个双向链表类:

既可以从前向后,也可以从后向前,所以在这个类中,即保存一下头结点,也保存一下尾结点的值

public class DoubleLinkedList {
    private int size;
    private DoubleNode head;
    private DoubleNode tail;
}

二、双向链表的增删改查

1.插入

头插

在当前链表的头部插入一个节点,让当前链表的头结点head前驱指向要插入的节点node,然后让node的后继指向head,然后让head = node,让node成为链表的头结点

在这里插入图片描述

代码如下:

/**
     * 头插
     */
    public void addFirst(int val){
        DoubleNode node = new DoubleNode(val);
        if (head == null){
            head = tail = node;
        }else{
            node.next = head;
            head.prev = node;
            head = node;
        }
        size++;
    }

尾插

和头插一样,只不过在链表的尾部插入

在这里插入图片描述

代码如下:

 public void addLast(int val){
        DoubleNode node = new DoubleNode(val);
        if (head == null){
            head = tail =node;
        }else{
            tail.next = node;
            node.prev = tail;
            tail = node;
        }
        size++;
    }

在index位置插入

在索引为index的位置插入值为val的节点:

插入还是要找前驱节点,但双向链表找前驱节点比单向链表找前驱节点要灵活很多,单向链表只能从头走到尾,假如此时有100个节点,要在索引为98的位置插入节点,那么双向链表就可以从尾结点开始找,会方便很多

如何判断从前向后找,还是从后向前找?

  • 1.index < size / 2 – >从前向后找,插入位置在前半部分
  • 2.index > size / 2 – >从后向前找,插入位置在后半部分

在这里插入图片描述

代码如下:

/**
     * 在index位置插入
     * @param index
     * @param val
     */
    public void add(int index,int val){
        DoubleNode cur = new DoubleNode(val);
        if (index < 0 || index > size){
            System.err.println("add index illegal");
            return;
        }else{
            if (index == 0){addFirst(val);}
            else if (index == size){addLast(val);}
            else{
                DoubleNode prev = node(index-1);
                DoubleNode next = prev.next;
                cur.next = next;
                next.prev = cur;
                prev.next = cur;
                cur.prev = prev;
            }
        }
        size++;
    }
/**
     * 根据索引值找到对应的结点
     * @param index
     * @return
     */
    private DoubleNode node(int index){
        DoubleNode x = null;
        if (index < size/2){
            x = head;
            for (int i = 0; i < index; i++) {
                x = x.next;
            }
        }else{
            x = tail;
            for (int i = size - 1; i > index ; i--) {
                x = x.prev;
            }
        }
        return x;
    }

2.修改

代码如下:

/**
     * 修改双向链表index位置的结点值为newVal
     */
    public int set(int index,int newVal){
        DoubleNode dummyHead = new DoubleNode();
        dummyHead.next = head;
        DoubleNode prev = dummyHead;
        DoubleNode cur = prev.next;
        if (index < 0 || index > size - 1){
            System.err.println("set index illegal");
        }else{
            for (int i = 0; i < index; i++) {
                prev = prev.next;
                cur = cur.next;
            }
        }
        int oldVal = cur.val;
        cur.val = newVal;
        return oldVal;
    }

3.查询

代码如下:

 /**
     * 查询index位置的结点值
     */
    public int get(int index){
        DoubleNode dummyHead = new DoubleNode();
        dummyHead.next = head;
        DoubleNode prev = dummyHead;
        DoubleNode cur = prev.next;
        if (index < 0 || index > size - 1){
            System.err.println("get index illegal");
        }else{
            for (int i = 0; i < index; i++) {
                prev = prev.next;
                cur = cur.next;
            }
        }
        return cur.val;
    }

4.删除

删除index位置的节点

代码如下:

//删除链表index位置的结点
    public void removeIndex(int index){
        if (index < 0 || index > size - 1){
            System.err.println("remove index illegal");
            return;
        }
        DoubleNode cur = node(index);
        unlink(cur);
    }
 /**
     * 删除当前双向链表的node结点
     * 分治法
     * @param node
     */
    private void unlink (DoubleNode node){
        DoubleNode prev = node.prev;
        DoubleNode successor = node.next;
        //1.先处理node的前半部分
        if (prev == null){
            head = successor;
        }else{
            //前驱不为空的情况
            prev.next = successor;
            node.prev = null;
        }
        if (successor == null){
            tail = prev;
        }else{
            successor.prev = prev;
            node.next = null;
        }
        size--;
    }

头删

调用删除任意位置的节点即可

代码如下:

//头删
    public void removeFirst(){
      removeIndex(0);
    }

尾删

调用删除任意位置的节点即可

代码如下:

//尾删
    public void removeLast(){
        removeIndex(size - 1);
    }

删除第一个值为val的节点

代码如下:

//删除第一个值为val的结点
    public void removeValOnce(int val){
        if (head == null){
            return;
        }
        for (DoubleNode x = head;x != null;x = x.next){
            if (x.val == val){
                unlink(x);
                break;
            }
        }
    }

 /**
     * 删除当前双向链表的node结点
     * 分治法
     * @param node
     */
    private void unlink (DoubleNode node){
        DoubleNode prev = node.prev;
        DoubleNode successor = node.next;
        //1.先处理node的前半部分
        if (prev == null){
            head = successor;
        }else{
            //前驱不为空的情况
            prev.next = successor;
            node.prev = null;
        }
        if (successor == null){
            tail = prev;
        }else{
            successor.prev = prev;
            node.next = null;
        }
        size--;
    }

删除所有值为val的值

代码如下:

//删除链表中所有值为val的结点
    public void removeAllVal(int val){
        for (DoubleNode x = head;x != null;){
            if (x.val == val){
                //暂存一下x的下一个结点
                DoubleNode next = x.next;
                unlink(x);
                x = next;
            }else{
                //val不是待删除的元素
                x = x.next;
            }
        }
    }
 /**
     * 删除当前双向链表的node结点
     * 分治法
     * @param node
     */
    private void unlink (DoubleNode node){
        DoubleNode prev = node.prev;
        DoubleNode successor = node.next;
        //1.先处理node的前半部分
        if (prev == null){
            head = successor;
        }else{
            //前驱不为空的情况
            prev.next = successor;
            node.prev = null;
        }
        if (successor == null){
            tail = prev;
        }else{
            successor.prev = prev;
            node.next = null;
        }
        size--;
    }

总结

本篇博客带大家了解了一下什么是双向链表,和单链表有什么区别,已经双向链表的一些基本的增删改查的操作,

到此这篇关于Java双向链表的操作的文章就介绍到这了,更多相关Java双向链表内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java两个数组合并为一个数组的几种方法

    java两个数组合并为一个数组的几种方法

    这篇文章主要给大家介绍了关于java两个数组合并为一个数组的几种方法,最近在写代码时遇到了需要合并两个数组的需求,文中将每种方法都介绍的非常详细,需要的朋友可以参考下
    2023-07-07
  • 一篇文章弄懂Java8中的时间处理

    一篇文章弄懂Java8中的时间处理

    Java8以前Java处理日期、日历和时间的方式一直为社区所诟病,将 java.util.Date设定为可变类型,以及SimpleDateFormat的非线程安全使其应用非常受限,下面这篇文章主要给大家介绍了关于Java8中时间处理的相关资料,需要的朋友可以参考下
    2022-01-01
  • java实现日期拆分的方法

    java实现日期拆分的方法

    这篇文章主要介绍了java实现日期拆分的方法,基于java日期类实现对日期字符串的拆分功能,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-07-07
  • 浅谈SpringBoot集成Redis实现缓存处理(Spring AOP实现)

    浅谈SpringBoot集成Redis实现缓存处理(Spring AOP实现)

    这篇文章主要介绍了浅谈SpringBoot集成Redis实现缓存处理(Spring AOP实现),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-12-12
  • JAVA异常体系结构详解

    JAVA异常体系结构详解

    Java把异常当作对象来处理,并定义一个基类java.lang.Throwable作为所有异常的超类,下面通过本文给大家分享JAVA异常体系结构,感兴趣的朋友一起看看吧
    2017-11-11
  • 解决Java API不能远程访问HBase的问题

    解决Java API不能远程访问HBase的问题

    这篇文章主要介绍了解决Java API不能远程访问HBase的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • Java程序包不存在的三种解决方法

    Java程序包不存在的三种解决方法

    有时候我们在导入程序之后,系统会给出错误提示:Java:程序包xxxx不存在,本文主要介绍了Java程序包不存在的三种解决方法,具有一定的参考价值,感兴趣的可以了解一下
    2024-02-02
  • SpringBoot ApplicationContext接口深入分析

    SpringBoot ApplicationContext接口深入分析

    ApplicationContext是Spring应用程序中的中央接口,由于继承了多个组件,使得ApplicationContext拥有了许多Spring的核心功能,如获取bean组件,注册监听事件,加载资源文件等
    2022-11-11
  • ThreadLocal原理介绍及应用场景

    ThreadLocal原理介绍及应用场景

    本文详细讲解了ThreadLocal原理介绍及应用场景,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-12-12
  • Java多线程编程之CountDownLatch同步工具使用实例

    Java多线程编程之CountDownLatch同步工具使用实例

    这篇文章主要介绍了Java多线程编程之CountDownLatch同步工具使用实例,需要的朋友可以参考下
    2015-05-05

最新评论