【Java数据结构】初始线性表之一:链表

为什么要有链表

上一节我们描述了顺序表:【Java数据结构】初识线性表之一:顺序表-CSDN博客

并且进行了简单模拟实现。通过源码知道,ArrayList底层使用数组来存储元素。

由于其底层是一段连续空间,当在ArrayList任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后搬移,时间复杂度为O(n),效率比较低,因此ArrayList不适合做任意位置插入和删除比较多的场景。因此:java集合中又引入了LinkedList,即链表结构。

链表

 链表的概念及结构

链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的 。

注意:

  • 链表的结构在逻辑上是连续的,但是在物理位置上不一定连续。
  • 节点一般都是从堆上申请出来的。
  • 从堆上申请空间是按一定策略来分配的,两次申请的空间可能会连续,也可能不连续。

实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:

  • 单向或者双向
  • 带头或者不带头
  • 循环或者非循环

本章节我们来描述其中两种:

  1. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。
  2. 无头双向链表:在Java的集合框架库中LinkedList底层实现就是无头双向循环链表。

模拟实现无头单向非循环链表

模拟实现无头单向非循环链表主要有以下的方法:

public class SingleLinkedList {
   //头插法
   public void addFirst(int data){
  }
   //尾插法
   public void addLast(int data){
  }
   //任意位置插入,第一个数据节点为0号下标
   public void addIndex(int index,int data){
  }
   //查找是否包含关键字key是否在单链表当中
   public boolean contains(int key){
       return false;
  }
   //删除第一次出现关键字为key的节点
   public void remove(int key){
  }

  //删除所有值为key的节点
   public void removeAllKey(int key){
  }
   //得到单链表的长度
   public int size(){
       return -1;
  }
   public void clear() {
  }
   
   public void display() {}
}

链表的插入:

 

链表的删除:

 模拟链表的代码实现:

public class MySingLeList {
    static class ListNode{
        public int val;
        public ListNode next;

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

    public void createList(){
        ListNode node1 = new ListNode(12);
        ListNode node2 = new ListNode(34);
        ListNode node3 = new ListNode(45);
        ListNode node4 = new ListNode(56);
        ListNode node5 = new ListNode(67);

        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        node4.next = node5;
        this.head = node1;
    }

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

    public int size(){
        ListNode tmp = this.head;
        int count = 0;
        while(!(tmp == null)){
            count++;
            tmp = tmp.next;
        }
        return count;
    }

    public boolean contains(int findData){
        ListNode tmp = this.head;
        while(!(tmp == null)){
            if(tmp.val == findData){
                return true;
            }
        }
        return false;
    }

    public void addFirst(int data){
        ListNode node = new ListNode(data);
        node.next = this.head;
        this.head = node;
    }

    public void addLast(int data){
        ListNode node = new ListNode(data);
        ListNode tmp = this.head;
        if(tmp == null){
            this.head = node;
            return;
        }
        while(!(tmp.next == null)){
            tmp = tmp.next;
        }
        tmp.next = node;
    }

    public void addIndex(int pos,int data){
        ListNode node = new ListNode(data);
        ListNode tmp = this.head;
        if(pos == 0){
            node.next = tmp;
            this.head = node;
            return;
        } else if (pos == this.size()) {
            this.addLast(data);
        } else if (pos > this.size()) {
            System.out.println("输入的位置错误!");
        }else{
            for (int i = 0; i < pos -1; i++) {
                tmp = tmp.next;
            }
            node.next = tmp.next;
            tmp.next = node;
        }
    }

    public void remove(int data){
        int flag = 1;
        ListNode tmp = this.head;
        if(this.head.val == data){
            this.head = this.head.next;
            return;
        }
        while(tmp.next != null){
            if(tmp.next.val== data){
                flag = 0;
                break;
            }
            tmp = tmp.next;
        }
        if(flag == 0){
            tmp.next = tmp.next.next;
        }else{
            System.out.println("链表中没有该元素!");
        }
    }
    public void removeAll(int data){
        ListNode prv = this.head;
        ListNode tmp = this.head.next;
        while(tmp != null){
            if(tmp.val == data){
                prv.next = tmp.next;
                tmp = tmp.next;
            }else{
                prv = tmp;
                tmp = tmp.next;
            }
        }
        if(this.head.val == data){
            this.head = this.head.next;
        }
    }

    public void clear(){
        this.head = null;
    }
}

模拟实现无头双向链表

无头双向链表主要有以下的方法:

public class MyLinkedList {
  //头插法
   public void addFirst(int data){ }
   //尾插法
   public void addLast(int data){}
   //任意位置插入,第一个数据节点为0号下标
   public void addIndex(int index,int data){}
   //查找是否包含关键字key是否在单链表当中
   public boolean contains(int key){}
   //删除第一次出现关键字为key的节点
   public void remove(int key){}
   //删除所有值为key的节点
   public void removeAllKey(int key){}
   //得到单链表的长度
   public int size(){}
   public void display(){}
   public void clear(){}
}

双向链表的插入:

 

双向链表的删除:

 

 模拟代码实现:

public class MyLinkedList {
    static class ListNode{
        private int val;
        private ListNode prev;
        private ListNode next;

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

        //头插法
        public void addFirst(int data){
            ListNode node = new ListNode(data);
            if(head == null){
                head = node;
                last = node;
            }else{
                node.next = head;
                head.prev = node;
                head = node;
            }
        }
        //尾插法
        public void addLast(int data){
            ListNode node = new ListNode(data);
            if(head == null){
                head = node;
                last = node;
            }else{
                last.next = node;
                node.prev = last;
                last = node;
            }
        }
        //任意位置插入,第一个数据节点为0号下标
        public void addIndex(int pos,int data){
            ListNode node = new ListNode(data);
            ListNode cur = head;
            if(pos == 0){
                addFirst(data);
            }else if(pos == this.size()){
                addLast(data);
            }else if(pos < 0 || pos > this.size()){
                System.out.println("插入位置错误!");
            }else{
                for (int i = 0; i < pos; i++) {
                    cur = cur.next;
                }
                cur.prev.next = node;
                node.prev = cur.prev;
                node.next = cur;
                cur.prev = node;
            }

        }
        //查找是否包含关键字key是否在单链表当中
        public boolean contains(int key){
            ListNode tmp = this.head;
            while(!(tmp == null)){
                if(tmp.val == key){
                    return true;
                }
            }
            return false;
        }
        //删除第一次出现关键字为key的节点
        public void remove(int key){
            ListNode cur = head;
            while(cur != null){
                if(cur.val == key){
                    if(cur == head){
                        head = head.next;
                        if(head != null){
                            head.prev =null;
                        }else{
                            last = null;
                        }
                        return;
                    }else if(cur == last){
                        last = last.prev;
                        last.next = null;
                        return;
                    }else{
                        cur.prev.next = cur.next;
                        cur.next.prev = cur.prev;
                        return;
                    }
                }else{
                    cur = cur.next;
                }
            }
            System.out.println("没有该元素!");
            return;
        }
        //删除所有值为key的节点
        public void removeAllKey(int key){
            ListNode cur = 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){
                        last = last.prev;
                        last.next = null;
                    }else{
                        cur.prev.next = cur.next;
                        cur.next.prev = cur.prev;
                    }
                }
                cur = cur.next;
            }
            System.out.println("没有该元素!");
            return;
        }
        //得到链表的长度
        public int size(){
            int count = 0;
            ListNode cur = head;
            while(cur != null){
                count++;
                cur = cur.next;
            }
            return count;
        }
        public void display(){
            ListNode tmp = this.head;
            while(!(tmp == null)){
                System.out.print(tmp.val+" ");
                tmp = tmp.next;
            }
        }
        public void clear(){
            ListNode cur = head;
            while(cur != null){
                ListNode curNext = cur.next;
                cur.prev = null;
                cur.next = null;
                cur = curNext;
            }
            head = null;
            last = null;
        }
}

LinkedList 的使用

什么是LinkedList

LinkedList的底层是双向链表结构(链表后面介绍),由于链表没有将元素存储在连续的空间中,元素存储在单独的节点中,然后通过引用将节点连接起来了,因此在在任意位置插入或者删除元素时,不需要搬移元素,效率比较高。

在集合框架中,LinkedList也实现了List接口,具体如下:

注意 :

  • 1. LinkedList实现了List接口
  • 2. LinkedList的底层使用了双向链表
  • 3. LinkedList没有实现RandomAccess接口,因此LinkedList不支持随机访问 
  • 4. LinkedList的任意位置插入和删除元素时效率比较高,时间复杂度为O(1)
  • 5. LinkedList比较适合任意位置插入的场景

LinkedList 的构造方法

public class Main {
    public static void main(String[] args) {
        List<Integer> list1 = new LinkedList<>();//无参构造

        List<Integer> list2 = new LinkedList<>();
        list2.add(1);
        list2.add(2);
        list2.add(3);
        List<Integer> list3 = new LinkedList<>(list2);//使用其他集合容器中元素构造List
        list3.add(4);
        System.out.println(list3);
    }
}

LinkedList 常用方法介绍

插入节点

public class Main {
    public static void main(String[] args) {
        List<Integer> list1 = new LinkedList<>();
        list1.add(5);
        list1.add(6);
        List<Integer> list2 = new LinkedList<>();
        list2.add(1);//尾插
        list2.add(2);
        list2.add(3);
        list2.add(1,4);//在指定位置插入节点
        list2.addAll(list1);//尾插其他容器中的所有节点
        System.out.println(list2);
    }
}

删除节点:

public class Main {
    public static void main(String[] args) {
        List<Integer> list2 = new LinkedList<>();
        list2.add(1);
        list2.add(2);
        list2.add(3);
        list2.remove(1);//删除指定位置的节点
        list2.remove(new Integer(3));//删除指定元素的节点
        System.out.println(list2);
    }
}

获取指定位置的元素:

public class Main {
    public static void main(String[] args) {
        List<Integer> list2 = new LinkedList<>();
        list2.add(1);
        list2.add(2);
        list2.add(3);

        System.out.println(list2.get(1));
    }
}

更新指定位置的元素:

public class Main {
    public static void main(String[] args) {
        List<Integer> list2 = new LinkedList<>();
        list2.add(1);
        list2.add(2);
        list2.add(3);
        list2.set(1,4);
        System.out.println(list2);
    }
}

判断指定元素是否在链表中:

public class Main {
    public static void main(String[] args) {
        List<Integer> list2 = new LinkedList<>();
        list2.add(1);
        list2.add(2);
        list2.add(3);
        System.out.println(list2.contains(new Integer(2)));
    }
}

截取部分 list

public class Main {
    public static void main(String[] args) {
        List<Integer> list2 = new LinkedList<>();
        list2.add(1);
        list2.add(2);
        list2.add(3);
        System.out.println(list2.subList(0,2));
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/803585.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

NGFW和防火墙的区别?

NGFW&#xff08;Next Generation Firewall&#xff0c;下一代防火墙&#xff09;和FW&#xff08;Firewall&#xff0c;防火墙&#xff09;在网络安全领域都扮演着重要角色&#xff0c;但它们在功能、性能和应用场景上存在显著的区别。以下是NGFW和FW之间的主要区别&#xff1…

数据库作业九

1、安装redis&#xff0c;启动客户端、验证。 2、string类型数据的命令操作&#xff1a; &#xff08;1&#xff09; 设置键值&#xff1a; &#xff08;2&#xff09; 读取键值&#xff1a; ​ &#xff08;3&#xff09; 数值类型自增1&#xff1a; ​ &#xff08;4&am…

NFT项目的第三方功能及接口

NFT项目涉及到第三方功能及接口的情况非常常见&#xff0c;因为NFT项目本身的功能通常是有限的&#xff0c;需要通过与第三方功能和接口的整合来实现更丰富的功能和更好的用户体验。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 以下…

【LeetCode】十六、并查集

文章目录 1、并查集Union Find2、并查集find的优化&#xff1a;路径压缩 Quick find3、并查集union的优化&#xff1a;权重标记4、leetcode200&#xff1a;岛屿数量解法一&#xff1a;DFS 1、并查集Union Find 并查集&#xff0c;一种树形的数据结构&#xff0c;处理不相交的两…

如何在excel表中实现单元格满足条件时整行变色?

可以试试使用条件格式&#xff1a; 一、条件格式 所谓“自动变色”就要使用条件格式。 先简单模拟数据如下&#xff0c; 按 B列数字为偶数 为条件&#xff0c;整行标记为蓝色背景色。 可以这样设置&#xff1a; 先选中1:10行数据&#xff0c;在这里要确定一下名称栏里显示…

DNS查询过程

DNS&#xff08;域名系统&#xff0c;Domain Name System&#xff09;是一个用于将域名和IP地址相互映射的系统。当你在浏览器中输入一个网址时&#xff0c;浏览器会通过DNS查询过程来找到对应的IP地址&#xff0c;以便能够连接到目标服务器。其查询过程一般通过以下步骤&#…

Apple Vision Pro 和其商业未来

机器人、人工智能相关领域 news/events &#xff08;专栏目录&#xff09; 本文目录 一、Vision Pro 生态系统二、Apple Vision Pro 的营销用例 随着苹果公司备受期待的进军可穿戴计算领域&#xff0c;新款 Apple Vision Pro 承载着巨大的期望。 苹果公司推出的 Vision Pro 售…

MySQL数据库慢查询日志、SQL分析、数据库诊断

1 数据库调优维度 业务需求&#xff1a;勇敢地对不合理的需求说不系统架构&#xff1a;做架构设计的时候&#xff0c;应充分考虑业务的实际情况&#xff0c;考虑好数据库的各种选择(读写分离?高可用?实例个数?分库分表?用什么数据库?)SQL及索引&#xff1a;根据需求编写良…

Qt窗口程序整理汇总

到今日为止&#xff0c;通过一个个案例的实验&#xff0c;逐步熟悉了 Qt6下 窗体界面开发的&#xff0c;将走过的路&#xff0c;再次汇总整理。 Qt Splash样式的登录窗https://blog.csdn.net/castlooo/article/details/140462768 Qt实现MDI应用程序https://blog.csdn.net/cast…

数据库课设---学生宿舍管理系统(sql server+C#)

1.引言 1.1 内容及要求 设计内容&#xff1a;设计学生宿舍管理系统。 设计要求&#xff1a; &#xff08;1&#xff09;数据库应用系统开发的需求分析&#xff0c;写出比较完善系统功能。 &#xff08;2&#xff09;数据库概念模型设计、逻辑模型设计以及物理模型设计。 …

【数学建模】——多领域资源优化中的创新应用-六大经典问题解答

目录 题目1&#xff1a;截取条材 题目 1.1问题描述 1.2 数学模型 1.3 求解 1.4 解答 题目2&#xff1a;商店进货销售计划 题目 2.1 问题描述 2.2 数学模型 2.3 求解 2.4 解答 题目3&#xff1a;货船装载问题 题目 3.1问题重述 3.2 数学模型 3.3 求解 3.4 解…

JS爬虫实战之极验四代

极验四代滑块验证码 一、目标网站说明二、流程步骤1. 逆向步骤一般分为&#xff1a;2. 接口确认1- 确认流程2- 获取verify的参数3- 构建requests验证verify的参数4- 锁定secode参数的作用 ok&#xff0c;让我们去获取verify接口中的响应&#xff01;&#xff01;&#xff01; 3…

el-table表格操作列错行处理

解决方法&#xff1a; <style>::v-deep .el-table th.el-table__cell > .cell {white-space: nowrap !important;} </style>

【C++航海王:追寻罗杰的编程之路】智能指针

目录 1 -> 为什么需要智能指针&#xff1f; 2 -> 内存泄漏 2.1 ->什么是内存泄漏&#xff0c;以及内存泄漏的危害 2.2 -> 内存泄漏分类 2.3 -> 如何避免内存泄漏 3 -> 智能指针的使用及原理 3.1 -> RAII 3.2 -> 智能指针的原理 3.3 -> std…

Kafka Producer发送消息流程之Sender发送线程和在途请求缓存区

文章目录 1. Sender发送数据1. 发送数据的详细过程&#xff1a;2. 关键参数配置 2. 在途请求缓存区 1. Sender发送数据 Sender线程负责将已经在RecordAccumulator中准备好的消息批次发送到Kafka集群。虽然消息在RecordAccumulator中是按照分区组织的&#xff0c;但Sender线程在…

【C++】类和对象的基本概念与使用

本文通过面向对象的概念以及通俗易懂的例子介绍面向对象引出类和对象。最后通过与之有相似之处的C语言中的struct一步步引出C中的类的定义方式&#xff0c;并提出了一些注意事项&#xff0c;最后描述了类的大小的计算方法。 一、什么是面向对象&#xff1f; 1.面向对象的概念 …

基于python的图像去水印

1 代码 import cv2 import numpy as npdef remove_watermark(image_path, output_path):# 读取图片image cv2.imread(image_path)# 转换为灰度图gray cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)# 使用中值滤波去除噪声median_filtered cv2.medianBlur(gray, 5)# 计算图像的梯…

Ambari Hive 创建函数无权限

作者&#xff1a;櫰木 1、创建udf函数 参考文档&#xff1a;https://blog.csdn.net/helloxiaozhe/article/details/102498567 如果已经编写好&#xff0c;请使用自己的。如果没有请参考以上链接进行udf函数编写。 2、创建函数遇到的问题 由于集群开启了kerberos&#xff0…

常用的点云预处理算法

点云预处理是处理点云数据时的重要部分&#xff0c;其目的是提高点云数据的质量和处理效率。通过去除离群点、减少点云密度和增强特征&#xff0c;可以消除噪声、减少计算量、提高算法的准确性和鲁棒性&#xff0c;从而为后续的点云处理和分析步骤&#xff08;如配准、分割和重…

[超级详细系列]ubuntu22.04配置深度学习环境(显卡驱动+CUDA+cuDNN+Pytorch)--[3]安装cuDNN与Pytorch

本次配置过程的三篇博文分享分别为为&#xff1a; [超级详细系列]ubuntu22.04配置深度学习环境(显卡驱动CUDAcuDNNPytorch)--[1]安装显卡驱动 [超级详细系列]ubuntu22.04配置深度学习环境(显卡驱动CUDAcuDNNPytorch)--[2]安装Anaconda与CUDA [超级详细系列]ubuntu22.04配置深…