【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);//使用其他集合容器中元素构造Listlist3.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.mzph.cn/diannao/47339.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

代码随想录二刷复习(二分法)

二分法模板&#xff1a; 1&#xff1a;左闭右闭区间写法 第一种写法&#xff0c;我们定义 target 是在一个在左闭右闭的区间里&#xff0c;也就是[left, right] &#xff08;这个很重要非常重要&#xff09;。 区间的定义这就决定了二分法的代码应该如何写&#xff0c;因为定…

vue 给特定满足条件的表单数据添加背景颜色,组件的 row-class-name

1、:row-class-name"tableRowClassName" 可为表格每行根据后面的函数绑定class名 <!-- 列表框 --><div class"tableList"><el-table :data"teamModelListTable" style"width: 100%"selection-change"handleSele…

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

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

不想填邀请码?Xinstall来帮你,一键安装无忧愁

在这个快节奏的时代&#xff0c;每一个点击都承载着用户的期待与耐心。然而&#xff0c;在下载App的过程中&#xff0c;繁琐的邀请码填写往往成为了用户体验的一大障碍。你是否也曾经因为不愿填写邀请码而放弃了一款心仪的App&#xff1f;今天&#xff0c;就让我们一起走进Xins…

镜像与容器

Docker Image (镜像) Docker 镜像概念 Docker iamge 本质上是一个 read-only 只读文件&#xff0c;这个文件包含了文件系统、源码、库文件、依赖、工具等一些运行 application 所必需的文件。 可以把 Docker image 理解成一个模板&#xff0c;可以通过这个模板实例化出来很多…

【Datawhale AI 夏令营】入门lightgbm及特征工程进行实践

文章目录 1. LightGBM简介2. 导入必要的库3. 加载数据集4. 数据可视化4.1 不同类型对应目标值的柱状图4.2 特定ID的目标值折线图 5. 特征工程5.1 合并训练集和测试集并进行排序5.2 历史平移5.3 窗口统计5.4 数据切分5.5 确定输入特征 6. 模型训练与评估7. 结果展示 1. LightGBM…

一文看懂JTAG基本知识

文章目录 1、JTAG是什么?1.1边界扫描2、JTAG如何起作用?2.1 PC控制JTAG2.2 并行端口2.3 JTAG TAP控制器2.4 计算JTAG链中元件个数2.5 获得JTAG链上芯片的ID3、边界扫描3.1、SAMPLE3.2、边界扫描寄存器3.3、JTAG还可以做什么?参考资料:1、JTAG是什么? JTAG是20世纪80年代开…

云监控(华为) | 实训学习day1(10)

云监控&#xff1a;确保服务器高效运行 在当今的数字化时代&#xff0c;服务器的稳定运行对于任何企业都至关重要。为了确保服务器的 CPU、内存和硬盘等资源的合理运行&#xff0c;云监控成为了一项不可或缺的技术。本文将详细介绍云监控的基本概念、所需软件、配置方法以及如何…

JDBC 技术 | Java连接MySQL数据库(四万字零基础保姆级超全详解)

文章目录 前言一. JDBC概述1. JDBC 概念2. JDBC 本质3. JDBC 的好处 二. JDBC 快速入门1. 编写Java 程序步骤2. 在IDEA 中的操作流程3. 正式编写 Java程序 三. JDBC API详解1. DriverManager 类2. Connection 接口2.1 获取执行SQL语句的对象 3 .ResultSet 类3.1 概述3.2 代码实…

Cadence23学习笔记(四)

这个人讲cadence也很不错&#xff1a; 73、创建Power NetClass[Cadence Allegro132讲视频教程字幕版]_哔哩哔哩_bilibili 上位机开发&#xff1a; MFC 最详细入门教程-CSDN博客 Board Geometry — Design_Outline 板框 Etch — Top 走线 Pin — Top 焊盘 …

探索APP开发中的主流版式设计与应用实践

在当今移动互联网高速发展的时代&#xff0c;APP已成为人们日常生活中不可或缺的一部分。无论是社交娱乐、购物支付还是工作学习&#xff0c;各类APP都以其独特的界面设计和用户体验赢得了用户的青睐。而APP开发的版式设计和页面规范&#xff0c;则是决定用户体验好坏的关键因素…

记录些MySQL题集(6)

MySQL 单表为什么不要超过 2000W 行&#xff1f; 数据持久化在磁盘中&#xff0c;磁盘的最小单元是扇区&#xff0c;一个扇区 0.5 KB&#xff0c;而由 8 个扇区可以构成一个文件系统块&#xff08;4K&#xff09;&#xff0c;以 InnoDB 存储引擎为例&#xff0c;一个数据页的大…

打卡第15天------二叉树

最近公司给我派活儿太多了,要干好多活儿,好多工作任务要处理,我都没时间刷题了。leetcode上的题目通过数量一直停留在原地不动,我真的很着急呀,我现在每天过的都有一种紧迫感,很着急,有一种与时间赛跑的感觉,真的时间过的太快了,没有任何人能够阻挡住时间的年轮向前推…

【异步爬虫:利用异步协程抓取一部电影】

利用异步协程抓取一部电影 我们把目光转向wbdy. 目前该案例还是可以用的. 我们想要抓取网上的视频资源就必须要了解我们的视频网站是如何工作的. 这里我用91看剧来做举例. 其他网站的原理是一样的. 1.视频网站是如何工作的 假设, 你现在想要做一个视频网站. 也有很多的UP猪…

【BUG】已解决:java.lang.IllegalStateException: Duplicate key

已解决&#xff1a;java.lang.IllegalStateException: Duplicate key 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班出身&#xff0c;就职于医疗科技公司&#xff0c;热衷分享知识&#xff0c;武汉城市…

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

目录 题目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 解…

超详细信息收集篇

1 域名信息收集 1.1 域名是什么 域名&#xff08;英语&#xff1a;Domain Name&#xff09;&#xff0c;又称网域&#xff0c;是由一串用点分隔的名字组成的 Internet 上某一台 计算机 或计算机组的名称&#xff0c;用于在数据传输时对计算机的定位标识&#xff08;有时也指地…

数据结构——栈和队列(C语言实现)

写在前面&#xff1a; 栈和队列是两种重要的线性结构。其也属于线性表&#xff0c;只是操作受限&#xff0c;本节主要讨论的是栈和队列的定义、表示方法以及C语言实现。 一、栈和队列的定义与特点 栈&#xff1a;是限定仅在表尾进行插入和删除的线性表。对栈来说&#xff0c;表…

【经验分享】关于静态分析工具排查 Bug 的方法

文章目录 编译器的静态分析cppcheck安装 cppcheck运行 cppcheck 程序员的日常工作&#xff0c;不是摸鱼扯皮&#xff0c;就是在写 Bug。虽然这是一个梗&#xff0c;但也可以看出&#xff0c;程序员的日常一定绕不开 Bug。而花更少的时间修复软件中的 Bug&#xff0c;且不引入新…

lightgbm

lightGBM 1.sklearn 使用代码 【机器学习基础】XGBoost、LightGBM与CatBoost算法对比与调参 首先&#xff0c;XGBoost、LightGBM和CatBoost都是目前经典的SOTA&#xff08;state of the art&#xff09;Boosting算法&#xff0c;都可以归类到梯度提升决策树算法系列。三个模…