【数据结构】链表LinkedList
创始人
2024-01-30 21:02:11

1.ArrayList的缺陷

2.单链表的实现

3.LinkedList的使用(模拟实现)

我们之前介绍过ArrayList了,它的底层是数组,数组是一段连续的空间,当我们想要插入或者删除数据的时候,插入元素,就要让插入位置的元素整体都往后移动,删除元素同样要让后面的元素往前移动,当要进行元素很多的插入或者删除的时候,ArrayList是效率很低的,所以当我们要大量插入元素或者大量删除元素,不推荐使用ArrayList

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

因此:java集合中又引入了LinkedList,即链表结构。

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

链表我们之前也介绍过,它在物理空间上是不连续,但在逻辑上连续,也就是说它的元素所在的存储空间中不是连在一起的,不是说元素1空间后面就是元素2的空间,但它在逻辑上连续,元素1跟元素2相当于拿一根绳子连接,虽然不是紧挨着的,但通过元素1就能找到元素2,这就像我们生活中的火车,或火车的车厢都是链在一起的

 链表中的元素我们称为节点,一个节点包含着本身的值,和下一个节点的信息(下一个节点的地址),分别是value和next(对于单链表而言)


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

(1)带头不带头

(2)单向双向

(3)循环非循环

我们把他们排列组合一下就能排列出8中情况,我们重要学习的链表结构有两种

1.不带头单向非循环

2.不带头双向非循环

自然这两种是面试笔试中常考的,当然也是比较难懂的,因为给的条件不多嘛~

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

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

这里解释一下这几个词的含义

带头 | | 不带头:头的意思就是头节点的意思,头节点的值一般没有什么含义,它与第一个元素链接,它拥有第一个元素的地址,只要有头,我们就能轻松找到第一个元素,你可以把它当做一个虚拟节点(虚拟节点的操作经常用来解题)

单向 || 双向:什么叫做单向,我们不是说了嘛,每一个节点它都包含下一个节点的地址,单向顾名思义就是一个方向,你只能从1找到2,而不能返回来从2找到1,因为1有2的地址,2只有3的地址,没有1的地址,所以说它是单向的;那么双向就是可以从1找到2,也可以从2找到1啦,双向链表的节点中,包含了两个地址,一个是下一个节点的地址,另外一个是上一个节点的地址

 上图就是单链表和双链表的图解

循环 | | 非循环:循环的意思就是这个链表围成了一个圈圈,下面给图来看

这图一目了然吧~上图就是循环,非循环自然不多解释


 接下来来自定义实现一个单链表

下面直接给出源码

package LinkedList内容;import ArrayList内容.PosIndexNotLegalException;/*** Created with IntelliJ IDEA.* Description:* User: 86152* Date: 2022-11-12* Time: 20:46*/
public class SingleList {//创建节点:包含值和地址两部分static class Node{public int val;public Node next;public Node(int val){this.val = val;}}public Node head;//创建头节点//用穷举的方式创建链表public void createList(){Node node1 = new Node(1);Node node2 = new Node(2);Node node3 = new Node(3);Node node4 = new Node(4);node1.next = node2;node2.next = node3;node3.next = node4;head = node1;}//头插法:时间复杂度O(1)public void addHead(int value){Node node = new Node(value);node.next = head;head = node;}//尾插法:时间复杂度O(n)public void addTail(int value){Node node = new Node(value);//处理没有节点的情况if(head == null){head = node;}else {Node cur = head;while (cur.next != null){cur = cur.next;}cur.next = node;}}//遍历打印链表public void display(){Node cur = this.head;while (cur != null){System.out.print(cur.val + " ");cur = cur.next;}System.out.println();}//判断链表中是否存在key值public boolean contains(int key){Node cur = head;while (cur != null){if(cur.val == key){return true;}cur = cur.next;}return false;}//链表长度public int size(){int count = 0;Node cur = head;while (cur != null){count++;cur = cur.next;}return count;}//在指定位置插入数据,要判断位置合法性private void checkIndex(int index){if(index < 0 || index > size()){throw new IndexNotLegalException();}}public void addIndex(int index, int value){checkIndex(index);if(index == 0){addHead(value);return;}if(index == size()){addTail(value);return;}Node node = new Node(value);Node cur = head;for(int i = 0; i < index-1; i++){cur = cur.next;}node.next = cur.next;cur.next = node;}//删除第一次出现key的节点//先找到前驱private Node prevKey(int key){Node cur = this.head;while (cur.next != null){if(cur.next.val == key){return cur;}cur = cur.next;}return null;}public void remove(int key){//头节点要单独处理if(head.val == key){head = head.next;return;}Node cur = prevKey(key);if(cur == null){return;}Node del = cur.next;cur.next = del.next;//cur.next = cur.next.next;}//删除所有值为key的节点//先处理其他节点,最后再处理头节点//对于头节点的处理得放在后面处理,放在前面最后不好处理public void removeAllKey(int key){if(head == null){return;}Node prev = head;Node cur = 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; 简单粗暴Node cur = head;while (cur != null){Node curNext = cur.next;cur.next = null;cur = curNext;}head = null;}//递归实现链表的打印public void printList(Node head){if(head == null){return;}if(head.next == null){System.out.print(head.val+" ");return;}printList(head.next);System.out.print(head.val+" ");}}

很多方法的细节都写在了注释里了~


我们下面来介绍一下LinkedList,我们在写题目的时候,通常少不了使用

LinkedList底层就是一个双向链表

双向链表的实现这里就不实现了,无非就是在单链表的基础上多加上一个prev,然后还有last(这是尾节点,和头节点head对应)

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

【说明】

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


下面介绍一下LinkedList的使用

这边直接给代码了,解释写在了注释里面

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;/*** Created with IntelliJ IDEA.* Description:* User: 86152* Date: 2022-11-14* Time: 17:36*/
public class LinkedListTest {//三种遍历方法public static void main(String[] args) {List list = new LinkedList<>();list.add(1);list.add(1);list.add(1);list.add(1);list.add(1);list.add(19);list.add(10);System.out.println("======for=========");for(int i = 0; i < list.size(); i++){System.out.print(list.get(i) + " ");}System.out.println();System.out.println("========foreach========");for(Integer x : list){System.out.print(x + " ");}System.out.println();System.out.println("==========迭代器========");ListIterator it = list.listIterator();while (it.hasNext()){System.out.print(it.next() + " ");}System.out.println();//反向迭代器打印ListIterator rit = list.listIterator(list.size());while (rit.hasPrevious()){System.out.print(rit.previous() + " ");}}/*** LinkedList常用api* @param args*/public static void main2(String[] args) {LinkedList list = new LinkedList<>();list.add(1);list.add(10);list.add(18);list.add(13);list.add(2);list.add(3);//add默认尾插System.out.println(list);list.add(0,2);//add的构造方法,支持在指定元素插入System.out.println(list);//删除操作remove//list.remove();//不带参数的remove默认删除第一个元素//list.remove(1);//带一个参数表示删除指定位置元素//list.removeFirst();//list.removeLast();//System.out.println(list);//查询contains 判断元素是否存在System.out.println(list.contains(18));//找到第一次出现元素的下标System.out.println(list.indexOf(2));//找第一次出现的位置System.out.println(list.lastIndexOf(3));//找最后一次出现的位置//获取指定位置元素System.out.println(list.get(0));//更新指定位置元素list.set(0,99);System.out.println(list);//截取元素System.out.println(list.subList(0, 2));//区间是前闭后开//还可以用截取的元素重新创建一个链表List list2 = list.subList(0,3);System.out.println(list2);//清空元素list.clear();System.out.println(list.size());}/*** 构造方法* @param args*/public static void main1(String[] args) {//一种是无参的构造方法LinkedList list = new LinkedList<>();//第二种:LinkedList的构造方法可以传入ArrayListArrayList arrayList = new ArrayList<>();arrayList.add(1);arrayList.add(2);arrayList.add(3);LinkedList linkedList = new LinkedList<>(arrayList);System.out.println(linkedList);}
}

 (部分图片来源:比特高博)

相关内容

热门资讯

长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
1秒多少毫秒 1秒等于多少毫秒... 长度单位换算1丈=10尺;1尺=10寸;1寸=10分;1分=10厘;1丈≈3.33米;1尺≈3.33...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
中国电信人工电话多少 中国电信... 当我们在使用中国移动、中国电信和中国联通提供的电信业务时,可能会发生争议。建议直接向服务提供商投诉,...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...