Java双向链表的操作

目录
  • 前言
  • 一、认识双向链表
  • 二、双向链表的增删改查
    • 1.插入
      • 头插
      • 尾插
      • 在index位置插入
    • 2.修改
    • 3.查询
    • 4.删除
      • 删除index位置的节点
      • 头删
      • 尾删
      • 删除第一个值为val的节点
      • 删除所有值为val的值
  • 总结

前言

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

一、认识双向链表

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

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

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

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

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双向链表内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • java数据结构基础:单链表与双向链表

    目录 单链表: 实现思路: 代码实现: 双向链表: 实现思路: 代码实现: 总结 单链表: 每个数据是以节点的形式存在的 每个节点分为数据域和指针域 数据域中保存该节点的数据 指针域中保存指向下一个节点的指针 实现思路: 节点类SingleNode中保存数据和指向下一个节点的指针 单链表类SingleLinkedList中保存链表的头节点,实现相关链表方法 对于链表方法,涉及到位置查找,如在指定位置增加.删除节点,需要使用一个临时变量temp从头节点开始遍历,直至找到对应的位置. 对于节点的增加

  • Java实现无头双向链表操作

    本文实例为大家分享了Java实现无头双向链表的具体代码,供大家参考,具体内容如下 无头双向链表的结构: 代码分析 节点结构 class Node {     private int data;     private Node next;     private Node prev;     public Node(int data) {         this.data = data;         this.prev = null;         this.next = null;  

  • Java实现双向链表

    本文实例为大家分享了Java实现双向链表的具体代码,供大家参考,具体内容如下 1.双向链表 1.1 双向链表的每个节点组成包含节点数据,上一个节点(pre),下一个节点(next) 1.2 双向链表节点结构 class Node { //节点数据data         int data;         Node pre;         Node next;         public Node(int data) {             this.data = data;      

  • Java数据结构与算法学习之双向链表

    目录 双向链表的储存结构示意图 双向链表的初始化结构 1.双向链表的结点 2.双向链表的头结点 3.总代码 双向链表中的指定文件插入元素  1.插入的为第一个位置 2.其他位置插入 总代码 双向链表的删除 1.删除第一个元素 2.删除其他位置元素 总代码 双向链表的储存结构示意图 双向链表的初始化结构 1.双向链表的结点 代码实现 //双向链表的结点,包含一个数据域,两个指针域 typedef struct DoublyNode { ElementType date; //数据域 struct

  • Java如何实现双向链表功能

    双向链表实现 双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱.所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点.注意:在操作双向链表时,不要去移动指向前驱节点和后继节点的指针,而是重新定义指向头尾的指针进行移动.* 环境 IDEA 自定义操作接口 双向链表. public interface MyList<E> { void add(E node); E get(int index); E remove(int ind

  • Java双向链表倒置功能实现过程解析

    题目要求:Java实现一个双向链表的倒置功能(1->2->3 变成 3->2->1) 提交:代码.测试用例,希望可以写成一个Java小项目,可以看到单元测试部分 该题目的代码,已经放到了我的github上,地址为:https://github.com/jiashubing/alibaba-linkedlist-reversed.git 最关键的是自定义节点Node 和自定义双向链表MyLinkedList 两个类,倒置的方法放在自定义链表类里reversed() ,具体的说明都在代

  • Java数据结构与算法之双向链表、环形链表及约瑟夫问题深入理解

    目录 一.双向链表 二.环形链表及其应用:约瑟夫问题 环形链表图示 构建一个单向的环形链表思路 遍历环形链表 约瑟夫问题 一.双向链表 使用带head头的双向链表实现 - 水浒英雄排行榜管理单向链表的缺点分析: 单向链表,查找的方向只能是一个方向,而双向链表可以向前或者向后查找. 单向链表不能自我删除,需要靠辅助节点,而双向链表,则可以自我删除,所以前面我们单链表删除节点时,总是找到temp,temp时待删除节点的前一个节点(认真体会). 分析双向链表的遍历,添加,修改,删除的操作思路 1.遍历

  • Java双向链表按照顺序添加节点的方法实例

    分析过程: 首先需要比较待添加的节点编号与已有的节点编号的大小,若待添加的节点编号已经存在,则不能加入.为防止出现空指针的情况,需要对节点的位置进行判断. 示例代码: package linkedlist; public class DoubleLinkedListDemo { public static void main(String[] args) { // 测试 System.out.println("双向链表的测试"); // 创建节点 Node node1 = new No

  • Java实现双向循环链表

    双向循环链表定义 相比于单链表,有两个指针,next指针指向下一个结点,prior指针指向上一个结点,最后一个结点的next指针指向头结点,头结点的prior指针指向最后一个结点 代码实现: 我们对单链表的实现加以修改 package algorithm.datastructure.doublelinkedlist; import java.util.NoSuchElementException; /* * 双向循环链表 * 两个指针,next指针指向下一个结点,prior指针指向上一个结点,最

  • Java双向链表的操作

    目录 前言 一.认识双向链表 二.双向链表的增删改查 1.插入 头插 尾插 在index位置插入 2.修改 3.查询 4.删除 删除index位置的节点 头删 尾删 删除第一个值为val的节点 删除所有值为val的值 总结 前言 我们之前学的单链表,默认只能从链表的头部遍历到链表的尾部,在实际中应用太少见,太局限:而双向链表,对于该链表中的任意节点,既可以通过该节点向前遍历,也可以通过该节点向后遍历,双向链表在实际工程中应用非常广泛,是使用链表这个结构的首选. 一.认识双向链表 单向链表不仅保存

  • Java编程cas操作全面解析

    CAS 指的是现代 CPU 广泛支持的一种对内存中的共享数据进行操作的一种特殊指令.这个指令会对内存中的共享数据做原子的读写操作. 简单介绍一下这个指令的操作过程:首先,CPU 会将内存中将要被更改的数据与期望的值做比较.然后,当这两个值相等时,CPU 才会将内存中的数值替换为新的值.否则便不做操作.最后,CPU 会将旧的数值返回.这一系列的操作是原子的.它们虽然看似复杂,但却是 Java 5 并发机制优于原有锁机制的根本.简单来说,CAS 的含义是"我认为原有的值应该是什么,如果是,则将原有的

  • Java使用poi操作excel实例解析

    本文实例为大家分享了Java使用poi操作excel的具体代码,供大家参考,具体内容如下 依赖poi的jar包,pom.xml配置如下: <project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0h

  • Java连接并操作Sedna XML数据库的方法

    本文实例讲述了Java连接并操作Sedna XML数据库的方法.分享给大家供大家参考.具体分析如下: Sedna 是一个原生的XML数据库,提供了全功能的核心数据库服务,包括持久化存储.ACID事务.索引.安全.热备.UTF8等.实现了 W3C XQuery 规范,支持全文搜索以及节点级别的更新操作. import ru.ispras.sedna.driver.*; public class SednaClient { public static void main(String args[])

  • java客户端Jedis操作Redis Sentinel 连接池的实现方法

    pom.xml配置 <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>1.0.2.RELEASE</version> </dependency> <dependency> <groupId>redis.clients<

  • java IO数据操作流、对象序列化、压缩流代码解析

    数据操作流 在io包中,提供了两个与平台无关的数据操作流: 数据输入流(DataInputStream) 数据输出流(DataOutputStream) 通常数据输出流会按一定格式将数据输出,再通过数据输入流按照一定格式将数据读入 DataOutputStream接口定义了一系列的writeXxx()的操作,可以写入各种数据类型的数据. 范例:使用数据操作流写入与读出数据 import java.io.DataOutputStream ; import java.io.File ; import

  • Java对象序列化操作详解

    本文实例讲述了Java对象序列化操作.分享给大家供大家参考,具体如下: 当两个进程在进行远程通信时,彼此可以发送各种类型的数据.无论是何种类型的数据,都会以二进制序列的形式在网络上传送.发送方需要把这个Java对象转换为字节序列,才能在网络上传送:接收方则需要把字节序列再恢复为Java对象. 只能将支持 java.io.Serializable 接口的对象写入流中.每个 serializable 对象的类都被编码,编码内容包括类名和类签名.对象的字段值和数组值,以及从初始对象中引用的其他所有对象

  • JAVA使用Ldap操作AD域的方法示例

    项目上遇到的需要在集成 操作域用户的信息的功能,第一次接触ad域,因为不了解而且网上其他介绍不明确,比较费时,这里记录下. 说明: (1). 特别注意:Java操作查询域用户信息获取到的数据和域管理员在电脑上操作查询的数据可能会存在差异(同一个意思的表示字段,两者可能不同). (2). 连接ad域有两个地址: ldap://XXXXX.com:389 和 ldap://XXXXX.com:636(SSL). (3). 端口389用于一般的连接,例如登录,查询等非密码操作,端口636安全性较高,用

  • Java后台线程操作示例【守护线程】

    本文实例讲述了Java后台线程操作.分享给大家供大家参考,具体如下: 一 点睛 有一种线程,它是后面运行的,它的任务是为其他线程提供服务,这种线程被称为"后台"线程,又称为"守护线程"或"精灵线程".JVM的垃圾回收线程就是典型的后台线程. 后台线程有个特征:如果所有的前台线程都死亡,后台线程会自动死亡, 调用Thread对象的setDaemon(true)方法可将指定线程设置成后台线程,所有的前台线程都死亡时,后台线程随之死亡.当整个虚拟机中只

  • Java线程组操作实例分析

    本文实例讲述了Java线程组操作.分享给大家供大家参考,具体如下: 一 点睛 1 Java使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制. 2 一旦某个线程加入了指定线程组之后,该线程将一直属于该线程组,直到该线程死亡,线程运行中途不能改变它所属的线程组. 3 Thread类提供了如下几个构造器来设置新创建的线程属于哪个线程组: Thread(ThreadGroup group, Runnable target):以target的run

随机推荐