数据结构 | LinkedList与链表

前言

  • ArrayList底层使用连续的空间,任意位置(尤其是0位置下标)插入或删除元素时,需要将该位置后序元素 整体往前或往后搬移,故时间复杂度为O(N). 优点(给定一个下标,可以快速查找到对应的元素,时间复杂度为O(1))
  • 增容需要申请新空间,拷贝数据 ,释放旧空间,会有不小的消耗.
  • 增容一般是呈2倍的增长,势必有一定的空间浪费.例如当前容量为100,满了要扩容到200,当我们再插入5个数据,后面没有数据插入了,那么就浪费了95个数据空间.

那么此时,我们就思考,有没有一种数据结构,可以随用随取,插入/删除数据可以不移动元素?👉👉

于此Java集合中又引入了链表结构.

链表的概念及结构

概念

链表是一种物理存储结构上非连续存储结构, 数据元素的逻辑顺序是通过链表中的引用链接次序实现的.简单来说就是内存上非连续,逻辑上连续的一种存储结构.
生活中,火车是由许许多多的 车厢 组成 ;在链表中,则是由许多的 节点 组成.

  • 每一个节点都有地址. 是一个对象 ; null表示 不指向任何一个对象.
  • 第一个节点叫做 头节点head.可以认为head就是一个变量,该变量里面存了节点的地址. head是一个引用,这个引用指向个节点对象,就可以把链表里所有的元素都遍历.

结构

组合一下有以下八种,但我们只需重点掌握 单向不带头非循环双向不带头非循环 结构.

链表的实现

创建LinkedList类 实现IList接口

复制代码
//模拟实现LinkedList链表
public class MySingleLinkedList implements IList {

    //定义一个静态内部类 --节点
    static class LinkedNode {
        public int value;//数值域
        public LinkedNode next;//next域 存放的是节点的地址

        public LinkedNode(int value) {
            this.value = value;
        }
    }
    public LinkedNode head;//head是 链表的头节点 null

    public void createList() {
        LinkedNode node1 = new LinkedNode(12);
        LinkedNode node2 = new LinkedNode(23);
        LinkedNode node3 = new LinkedNode(34);
        LinkedNode node4 = new LinkedNode(45);
        node1.next = node2;//表示node1的下一个节点是node2 node2这个引用存储了地址
        node2.next = node3;
        node3.next = node4;
        this.head = node1;//head指向了第一个节点的地址
    }

     @Override
    public void display() {
        LinkedNode cur = head;//定义临时变量
        while(cur != null) {   //当头节点为null时 算把所有节点都遍历完了
            System.out.print(cur.value + " ");
            cur = cur.next;//从第一个节点走到下一个节点
        }
    }

     @Override
    public int size() {   //计算有多少个节点
        int count = 0;
        LinkedNode cur = head;
        while(cur != null) {
            count ++;
            cur = cur.next;
        }
        return count;
    }

}

public interface IList {
        void addFirst(int data);
        void addLast(int data);
        void addIndex(int index,int data);
        boolean contains(int key);
        void remove(int key);
        void removeAllKey(int key);
        int size();
        void clear();
        void display();
}

解析

  1. 在构造方法中,为什么不初始化next域? 是因为,当我们去插入一个节点时,首先要实例化该节点,但在构造完成这个节点之后,不知道这个next会是谁,所以next域的值应该为null.
  2. **head是链表的头节点.**是链表的属性,不是节点的节点. 初始值也为null.
  1. 实例化了四个节点的对象.
  2. node1的下一个节点是node2 , 用代码表示为 node1.next = node2; 同时node2这个引用变量存储的是0x987.
  3. this.head = node1; 表示 head 指向了第一个节点的地址.
  1. head = head.next ; 从第一个节点走到 下一个节点.
  2. head = null ; 当head头节点为null时表示所有节点都被遍历了.
  3. 定义临时变量cur , 当遍历完后 head = null了就不指定头节点的位置了,我们希望的是 能够找到头节点的位置使得head始终指向第一个节点.
  4. 插入数据的时候 一定要**先绑后面(该节点的下一个)**的,因为可以通过遍历节点找到所有的节点 ; 而先把 头节点head这个引用指向该节点本身 , 再把该节点下一个指向head就找不到了,因为所指的是本身,后面的数据丢失了..
相关推荐
姑苏洛言19 分钟前
扫码点餐小程序产品需求分析与功能梳理
前端·javascript·后端
仪器科学与传感技术博士21 分钟前
Matplotlib库:Python数据可视化的基石,发现它的美
开发语言·人工智能·python·算法·信息可视化·matplotlib·图表可视化
Java技术小馆24 分钟前
PromptPilot打造高效AI提示词
java·后端·面试
whysqwhw28 分钟前
线程池数量配置
java
陈陈陈同学241 小时前
Vercel迁移到Dokploy自部署,每月立省20刀
后端·node.js
计算机毕设定制辅导-无忧学长1 小时前
InfluxDB 权限管理与安全加固(一)
java·struts·安全
老华带你飞2 小时前
生产管理ERP系统|物联及生产管理ERP系统|基于SprinBoot+vue的制造装备物联及生产管理ERP系统设计与实现(源码+数据库+文档)
java·数据库·vue.js·论文·制造·毕设·生产管理erp系统
一勺-_-2 小时前
全栈:如何判断自己应该下载哪个版本的Tomcat
java·tomcat
倔强的皮皮虾2 小时前
sharding proxy 实战读写分离,分库分表
后端
ONE_Gua2 小时前
魔改chromium源码——解除 iframe 的同源策略
前端·后端·浏览器