数据结构-3.链表

前言

本篇博客给大家带来的是链表的知识点, 其中包括面试经常会提问的真题 ArrayList 和 LinkedList 的区别 .

文章专栏: Java-数据结构

若有问题 评论区见

欢迎大家点赞 评论 收藏 分享

如果你不知道分享给谁,那就分享给薯条, 如果分享不成功, 那我就会回你一下,那样你就分享成功啦.

你们的支持是我不断创作的动力 .

1.ArrayList的存在的问题

1.ArrayList底层使用连续的空间, 任意位置插入或者删除元素时, 需要将该位置后面的元素整体往前或往后移动,故时间复杂度为 O(N) .

2.增容需要申请新的空间, 拷贝数据, 释放旧空间 会有不小的消耗 .

3.增容一般是呈1.5倍或2倍增长,势必会有一定的空间浪费. 例如当前容量为100时, 假设呈二倍增长,满了以后增容到200, 我们再继续插入5个数据, 后面没有数据插入的话就浪费了95个数据空间.

如果我们想要达到那种随用随取, 有几个数据我就增几个空间,那我们该怎么办呢? - 就是今天要介绍的链表了

2.链表

2.1链表的概念及结构

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

注意: 

1. 从上图可看出, 链式结构再逻辑上是连续的的,但是在物理上不一定连续.

2. 现实中的结点一般都是从堆上申请出来的

3. 从堆上申请的空间, 是按照一定的策略来分配的, 两次申请的空间可能连续, 也可能不连续

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

1. 单向或者双向

2.带头或者不带头

3.循环或者非循环

虽然有8种链表的结构, 但是我们重点掌握两种: 

1.无头单向非循环链表: 结构简单, 一般不会单独用来存数据. 实际种更多的是作为其他数据结构的子结构, 如: 哈希桶, 图的邻接表等等. 另外这种结构在笔试 面试中出现很多.

2.无头双向链表: 在Java的集合框架库中LinkedList 底层实现 就是无头双向循环链表.

2.2链表的实现(无头单向非循环链表)

写到这里, 我想起上篇 顺序表的实现 , 我直接把代码一丢 没做任何解释, 会让初学者看得很难受, 所以这次我得改正这个错误, 尽量让大家都能看懂并且会自己写. 其实解释也是很耗费时间的, 作为一名大二的学生😭, 上专业课和看网课就让我的时间所剩无几, 完后还要写blog, 我真是累成狗了🐶. 但是我还是很开心的,  一方面能够给大家带来知识, 另一方面也是在巩固我的知识. 

第一步创建一个类 MySingleList 

 我们都知道节点是链表里面的 , 所以节点就是链表的成员,这没有问题,

那问题来了, 节点中的数据data呢? 它可以定义为链表的成员吗? 

显然是不行的, 因为data是节点里面的, 只需要,在MySingleList类当中再定义一个内部类ListNode就可以解决了.

public class MySingleList {//因为节点是链表的一部分,所以可以将其定义为内部类static class ListNode {public int val;//节点的值域public ListNode next;//下一个节点的地址public ListNode(int val) {this.val = val;}}
public ListNode head;//表示当前链表的头节点
}

所以有了上述代码. 那么接着我想要实现链表的头插法, 尾插法. 我发现没有链表,

那么就自己手动实现一个简单点的,

// 头插法
public void addFirst ( int data ){
}
// 尾插法
public void addLast ( int data ){
}
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(23);ListNode node3 = new ListNode(34);ListNode node4 = new ListNode(45);ListNode node5 = new ListNode(56);node1.next = node2;node2.next = node3;node3.next = node4;node4.next = node5;this.head = node1;}
}
好,  到这里 我们就可以来实现链表的基本操作: 

第二步实现链表的基本操作

遍历单链表:

//遍历单链表public void display() {//指向头节点的引用不能动,所以利用cur代替ListNode cur = this.head;while(cur != null) {System.out.print(cur.val+" ");cur = cur.next;}System.out.println();}

求链表的长度:

//得到单链表的长度public int size(){int count = 0;ListNode cur = this.head;while(cur != null) {count++;cur = cur.next;}return count;}

查找关键字key是否包含在链表中:

//查找关键字key是否包含在单链表当中public boolean contains(int key) {ListNode cur = this.head;while (cur != null) {if(key == cur.val) {return true;}cur = cur.next;}return false;}

头插法:

//头插法public void addFirst(int data) {ListNode node = new ListNode(data);node.next = head;head = node;}

尾插法:

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

任意位置插入,第一个数据节点为0号下标: 

//任意位置插入,第一个数据节点为0号下标public void addIndex(int index, int data) {if(index < 0 || index > size()) {System.out.println("index 不合法");return;}if(index == 0) {addFirst(data);return;}if(index == size()) {addLast(data);return;}int count = 0;ListNode node = new ListNode(data);ListNode cur = this.head;while(count != index-1) {cur = cur.next;count++;}node.next = cur.next;cur.next = node;}

清除链表, 直接把head赋值为空即可:

//清除单链表public void clear() {this.head = null;}

删除第一个值为key的节点:

//删除第一个关键字为key的节点public void remove(int key){if(head == null) {return;}//单独删除头节点if(head.val == key) {head = head.next;return;}ListNode cur = searchPrev(key);if(cur == null) {System.out.println("没有找到key");return;}ListNode del = cur.next;cur.next = del.next;}//找到关键字key的前驱private ListNode searchPrev(int key) {ListNode cur = this.head;while(cur.next != null) {if(cur.next.val == key) {return cur;}cur = cur.next;}return null;}

删除值为key的所有节点:

 //删除所有关键为key的节点public void removeAll(int key){if(head == null) {return;}ListNode prev = this.head;ListNode cur = this.head.next;while(cur != null) {if(cur.val == key) {prev.next = cur.next;cur = cur.next;}else {prev = cur;cur = cur.next;}}if(head.val == key) {head = head.next;}}

下面是全部代码:

//因为节点是链表的一部分,所以可以将其定义为内部类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(23);ListNode node3 = new ListNode(34);ListNode node4 = new ListNode(45);ListNode node5 = new ListNode(56);node1.next = node2;node2.next = node3;node3.next = node4;node4.next = node5;this.head = node1;}//遍历单链表public void display() {//指向头节点的引用不能动,所以利用cur代替ListNode cur = this.head;while(cur != null) {System.out.print(cur.val+" ");cur = cur.next;}System.out.println();}//得到单链表的长度public int size(){int count = 0;ListNode cur = this.head;while(cur != null) {count++;cur = cur.next;}return count;}//查找关键字key是否包含在单链表当中public boolean contains(int key) {ListNode cur = this.head;while (cur != null) {if(key == cur.val) {return true;}cur = cur.next;}return false;}//头插法public void addFirst(int data) {ListNode node = new ListNode(data);node.next = head;head = node;}//尾插法public void addLast(int data) {ListNode node = new ListNode(data);ListNode  cur = this.head;if(cur == null) {head = node;return;}while(cur.next != null) {cur = cur.next;}cur.next = node;node.next = null;}//任意位置插入,第一个数据节点为0号下标public void addIndex(int index, int data) {if(index < 0 || index > size()) {System.out.println("index 不合法");return;}if(index == 0) {addFirst(data);return;}if(index == size()) {addLast(data);return;}int count = 0;ListNode node = new ListNode(data);ListNode cur = this.head;while(count != index-1) {cur = cur.next;count++;}node.next = cur.next;cur.next = node;}//删除第一个关键字为key的节点public void remove(int key){if(head == null) {return;}//单独删除头节点if(head.val == key) {head = head.next;return;}ListNode cur = searchPrev(key);if(cur == null) {System.out.println("没有找到key");return;}ListNode del = cur.next;cur.next = del.next;}//找到关键字key的前驱private ListNode searchPrev(int key) {ListNode cur = this.head;while(cur.next != null) {if(cur.next.val == key) {return cur;}cur = cur.next;}return null;}//删除所有关键为key的节点public void removeAll(int key){if(head == null) {return;}ListNode prev = this.head;ListNode cur = this.head.next;while(cur != null) {if(cur.val == key) {prev.next = cur.next;cur = cur.next;}else {prev = cur;cur = cur.next;}}if(head.val == key) {head = head.next;}}//清除单链表public void clear() {this.head = null;}

3.LinkedList的模拟实现(无头双向链表)

无头双向链表跟上述的单向链表的实现大致相同,不过是多了一个前驱,故不做解释

static class ListNode {private int val;//数据域private ListNode prev;//前驱private ListNode next;//后继public ListNode(int val) {this.val = val;}}public ListNode head;//双向链表的头节点.public ListNode last;//双向链表的尾节点.//头插法public void addFirst(int data){ListNode node = new ListNode(data);if(head == null) {head = node;last = node;}else{head.prev = node;node.next = head;head = node;}}//尾插法public void addLast(int data){ListNode node = new ListNode(data);if(head == null) {last = node;head = node;}else {last.next = node;node.prev = last;last = node;}}//任意位置插入,第一个数据节点为0号下标public void addIndex(int index,int data){ListNode node = new ListNode(data);ListNode cur = head;if(index == 0) {addFirst(data);return;}if(index == size()) {addLast(data);return;}if(index != 0 && index != size()) {for (int i = 0; i < index-1; i++) {cur = cur.next;}node.next = cur.next;cur.next.prev = node;cur.next = node;node.prev = cur;}}
//判断Index是否合法private void checkIndex(int index) {if(index < 0 || index > size()) {throw new IndexOutOfException("index 不合法" + index);}}//查找是否包含关键字key是否在链表当中public boolean contains(int key){ListNode cur = head;while(cur != null) {if(cur.val == key) {return true;}cur = cur.next;}return false;}//删除第一次出现关键字为key的节点public void remove(int key){ListNode cur = head;if(head.val == key) {head = head.next;if(head != null) {//考虑只有一个节点的情况下head.prev = null;}else{last = null;}return;}if(last.val == key) {last = last.prev;last.next = null;return;}while(cur != null) {if(cur.val == key) {cur.prev.next = cur.next;cur.next.prev = cur.prev;return;}cur = cur.next;}}//删除所有值为key的节点public void removeAllKey(int key){ListNode cur = head;while(cur != null) {if(cur.val == key) {if(cur == head) {head = head.next;head.prev = null;cur = cur.next;continue;}if(cur == last) {last = last.prev;last.next = null;cur = cur.next;continue;}cur.prev.next = cur.next;cur.next.prev = cur.prev;cur = cur.next;}cur = cur.next;}}//得到链表的长度public int size(){ListNode cur = head;int count = 0;while(cur != null) {cur = cur.next;count++;}return count;}//遍历链表public void display(){ListNode cur = head;for (int i = 0; i < size(); i++) {System.out.print(cur.val + " ");cur = cur.next;}System.out.println();}//清除链表public void clear(){/*ListNode cur = head;while(cur.next != null) {cur = cur.next;cur.prev.next = null;cur.prev = null;}//while循环走出来有两种情况://1.head.next = null;//2.cur走到了尾节点.if(head.next == null) {head = head.next;}else {cur.prev = null;}head = null;last = null;*///gaobo写法:ListNode cur = head;while(cur != null) {ListNode curNext = cur.next;cur.prev = null;cur.next = null;cur = curNext;}head = null;last = null;}

4.LinkedList的使用

4.1LinkedList的介绍

LinkedList的官方文档:

LinkedList (Java Platform SE 8 ) (oracle.com)

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

说明:

1. LinkedList实现了List接口

2. LinkedList的底层使用了双向链表

3. LinkedList没有实现RandomAccess接口, 因此LinkedList不支持随机访问

4. LinkedList的任意位置插入和删除元素时的效率比较高,时间复杂度为O(1)

5. LinkedList比较适合任意位置插入的场景

4.2LinkedList的使用

1.LinkedList的构造

public class Test3 {public static void main(String[] args) {//构造一个空的LinkedListList<Integer> list1 = new LinkedList<>();List<String> list2 = new ArrayList<>();list2.add("2334");list2.add("ieie");list2.add("uuy");//使用ArrayList构造LinkedListList<String> list3 = new LinkedList<>(list2);System.out.println(list3);}
}

第二种构造方法与 顺序表中第二种构造方法相似. 这里看不懂的可以去上篇看看顺序表中关于构造方法的讲解.

2.LinekedList的其他常用方法

int indexOf(Object o)    返回第一个o所在下标    
int lastlndexOf(Object 0)    返回最后一个o的下标    
List<E> subList(int fromlndex, int tolndex)    截取部分 list

5.ArrayList和LinkedList的区别

从不同点处记忆即可

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

c++与cmake:完整的C++项目构建注意事项

个人博客:Sekyoro的博客小屋 个人网站:Proanimer的个人网站 最近常常使用cmake构建c项目有感,从创建项目到打包发布总结一下需要注意的事情. 项目组织方式 具体的项目组织方式因人而异,这里推荐一种,在src目录中创建模块目录,再在include目录中常见对应的同名目录包含头文件,…

阿里巴巴API助力电商:商品详情获取与数据驱动的完美结合

阿里巴巴API在电商领域的应用&#xff0c;特别是在商品详情获取与数据驱动的决策过程中&#xff0c;发挥着至关重要的作用。以下是对这一主题的详细阐述&#xff1a; 一、阿里巴巴API在商品详情获取中的应用 丰富的数据支持&#xff1a; 阿里巴巴提供的商品详情API&#xff0…

html详细知识

1-标题标签、水平线、字体标签 <!--1.标题标签1&#xff09;格式&#xff1a;<hn></hn> n的范围是1-6&#xff0c;依次递减2&#xff09;标题标签特点&#xff1a;a:单独占一行b:自动加粗2.水平线1&#xff09;格式&#xff1a;<hr/>2)属性&#xff1a;…

深度学习对抗海洋赤潮危机!浙大GIS实验室提出ChloroFormer模型,可提前预警海洋藻类爆发

2014 年 8 月&#xff0c;美国俄亥俄州托莱多市超 50 万名居民突然收到市政府的一则紧急通知——不得擅自饮用自来水&#xff01; 水是人类生存的基本供给&#xff0c;此通告关系重大&#xff0c;发出后也引起了不小的恐慌。究其原因&#xff0c;其实是美国伊利湖爆发了大规模…

如何使用ssm实现在线视频网站开发

TOC ssm631在线视频网站开发jsp 绪论 1.1 选题背景 当人们发现随着生产规模的不断扩大&#xff0c;人为计算方面才是一个巨大的短板&#xff0c;所以发明了各种计算设备&#xff0c;从结绳记事&#xff0c;到算筹&#xff0c;以及算盘&#xff0c;到如今的计算机&#xff0…

关于嵌入式硬件需要了解的基础知识

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///C爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于嵌入式硬件基础知识的相关内容&#xff…

html,css基础知识点笔记(二)

9.18&#xff08;二&#xff09; 本文主要教列表的样式设计 1&#xff09;文本溢出 效果图 文字限制一行显示几个字&#xff0c;多余打点 line-height: 1.8em; white-space: nowrap; width: 40em; overflow: hidden; text-overflow: ellipsis;em表示一个文字的大小单位&…

828华为云征文|云服务器Flexus X实例|Ubunt部署Vue项目

概要 本章将深入阐述Vue项目在Ubuntu环境下&#xff0c;实现在华为云Flexus X云服务器上的部署过程&#xff0c;此次演示以Vue.js项目为核心华为云在已经到来的828 B2B企业节上&#xff0c;为Vue等前端项目的部署与运维提供强有力的支持。 Ubuntu部署Vue项目的影响&#xff1…

VS Code远程连接虚拟机

VS Code远程连接虚拟机 1.下载vscode2.打开VS Code下载Remote-SSH插件1.修改相关信息 3.虚拟机检查或安装ssh4.检查虚拟机服务是否安装成功5.开启ssh&#xff0c;并检查是否开启成功 1.下载vscode 2.打开VS Code下载Remote-SSH插件 1.修改相关信息 2. 3.虚拟机检查或安装ssh…

封装svg图片

前言 项目中有大量svg图片&#xff0c;为了方便引入&#xff0c;所以对svg进行了处理 一、svg是什么&#xff1f; svg是可缩放矢量图形&#xff0c;是一种图片格式 二、使用步骤 1.创建icons文件夹 将icons文件夹放进src中&#xff0c;并创建一个svg文件夹和index.js&…

PMP--一模--解题--161-170

文章目录 13.干系人管理161、 [单选] 项目经理正在领导一个公司内部项目&#xff0c;该项目正处于早期阶段。该项目与一年前结束的另一个项目很相似&#xff0c;项目经理该做什么来分析涉及的干系人&#xff1f; 10.沟通管理162、 [单选] 在项目执行过程中&#xff0c;一位关键…

Docker安装 ▎Docker详细讲解 ▎数据卷挂载 ▎Nginx安装理解

前言 Docker是一种容器化技术&#xff0c;简化软件的部署和管理。文章详细解释了Docker的架构、安装步骤和常用命令&#xff0c;帮助用户快速启动和管理容器。还介绍了Docker镜像命令和数据卷挂载的实例&#xff0c;增强对持久化存储的理解&#xff0c;并涵盖了Nginx的安装方法…

『功能项目』QFrameWork框架重构OnGUI【63】

我们打开上一篇62QFrameWork背包框架的项目&#xff0c; 上文将功能实现在一个脚本中 本章要做的事情让脚本实现背包框架思想 首先按照图示创建脚本&#xff1a; 创建脚本&#xff1a;Item.cs namespace QFramework {public class Item{//道具public string Key;public string …

【网络】传输层协议TCP

TCP协议 TCP&#xff08;Transmission Control Protocol&#xff0c;传输控制协议&#xff09;是一种面向连接的、可靠的、基于字节流的传输层通信协议&#xff0c;由IETF的RFC 793定义。TCP在IP&#xff08;Internet Protocol&#xff0c;互联网协议&#xff09;网络层上提供…

最长连续子序列 - 华为OD统一考试(E卷)

OD统一考试&#xff08;E卷&#xff09; 分值&#xff1a; 100分 题解&#xff1a; Java / Python / C 2024华为OD机试&#xff08;E卷D卷C卷&#xff09;最新题库【超值优惠】Java/Python/C合集 题目描述 有N个正整数组成的一个序列。给定整数sum&#xff0c;求长度最长的连续…

海报制作哪个软件好?探索5个免费海报生成器

Hey&#xff0c;最近发现了几个超酷的海报生成器&#xff0c;它们简直是设计新手的救星&#xff01;无论是想要快速制作一张吸引眼球的促销海报&#xff0c;还是为即将到来的活动设计一张有创意的邀请函&#xff0c;这些工具都能让整个过程变得既简单又有趣。 设想一下&#x…

React框架搭建,看这一篇就够了,看完你会感谢我

传统搭建框架的方式 在2024年以前&#xff0c;我们构建框架基本上采用官方脚手架&#xff0c;但是官方脚手架其实大概率都不符合我们的项目要求&#xff0c;搭建完了以后往往需要再继续集成一些第三方的包。这时候又会碰到一些版本冲突&#xff0c;配置教程等&#xff0c;往往…

PMP--二模--解题--1-10

文章目录 4.整合管理--商业文件--商业论证&#xff08;是否值得所需投资、高管们决策的依据&#xff09;反映了&#xff1a;1、 [单选] 收到新项目的客户请求之后&#xff0c;项目经理首先应该做什么&#xff1f; 14.敏捷--角色--产品负责人PO–职责–1.创建待办列表并排序;2.确…

大数据-137 - ClickHouse 集群 表引擎详解2 - MergeTree 存储结构 一级索引 跳数索引

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

k8s的一些命令

kubectl get nodes &#xff1a;查看节点的状态 查看Pod的状态&#xff1a; kubectl get pod --all -namespacesPending,ContainerCreating,ImagePullBackOff都表明Pod没有就绪&#xff0c;Running才是就绪状态 查看Pod的具体情况&#xff1a; kubectl describe pod podnamek…