当前位置: 首页 > news >正文

【数据结构】第四弹——LinkedList与链表

文章目录

  • 一. ArrayList 的缺陷
  • 二.链表
    • 2.1 链表的概念及结构
    • 2.2 链表的结构
      • 2.2.1 单向或者双向
      • 2.2.2 带头或者不带头
      • 2.2.3 循环非循环
    • 2.3 链表的实现
      • 1. IList接口
      • 2. MySingleList 类中具体实现(不带头单向非循环链表)
        • 1. 节点抽象成内部类
        • 手搓一个链表
        • 2. 头插法
        • 3. 尾插法
        • 4. 指定位置插入
        • 5. 是否包含key值
        • 6. 删除指定值的节点
        • 7. 删除所有指定值所在节点
        • 8. 获取链表长度
        • 9. 清空链表
        • 10. 打印链表
  • 三. 链表面试题
  • 四. MyLinkedList 的模拟实现(不带头双向非循环链表)
    • 节点抽象成内部类
    • 1.头插法
    • 2.尾插法
    • 3.指定位置插入
    • 4.是否包含Key值
    • 5. 删除指定值的节点
    • 6.删除所有指定值的节点
    • 7. 获取链表长度
    • 8. 清空链表
    • 9.打印链表
  • 五. LinkedList 的使用
    • 5.1 什么是LinkedList
    • 5.2 LinkedList 的使用
    • 5.2.1 LinkedList 的构造
    • 5.2.2 LinkedList的其他常用方法介绍
    • 5.3 LinkedList的遍历
      • 5.3.1 for遍历
      • 5.3.2 foreach
      • 5.3.3 迭代器遍历(正向反向)
  • 六. ArrayList和LinkedList的区别

一. ArrayList 的缺陷

上篇文章已经熟悉了ArrayList的使用,并且进行了简单模拟实现。通过源码知道,ArrayList底层使用数组来存储元素

ArrayList源码:

public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable{// ...//  默认容量是10private static final int DEFAULT_CAPACITY = 10;//...// 数组:用来存储元素transient Object[] elementData; // non-private to simplify nested class access// 有效元素个数private int size;public ArrayList(int initialCapacity) {if (initialCapacity > 0) {this.elementData = new Object[initialCapacity];} else if (initialCapacity == 0) {this.elementData = EMPTY_ELEMENTDATA;} else {throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);}
}

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

二.链表

2.1 链表的概念及结构

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

在这里插入图片描述

1.从图中可以看出,链式结构在逻辑上是连续的,但是在物理上不一定连续
2.现实中的节点一般都是从堆上申请出来的
3.从堆上申请的空间,是按照一定的策略来分配的,两次申请的空间可能连续,也可能不连续

2.2 链表的结构

以下情况组合起来就有8种链表结构:

2.2.1 单向或者双向

在这里插入图片描述

2.2.2 带头或者不带头

在这里插入图片描述

2.2.3 循环非循环

在这里插入图片描述

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

  • 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多
    在这里插入图片描述
  • 无头双向链表:在Java的集合框架库中LinkedList底层实现就是无头双向循环链表

2.3 链表的实现

1. IList接口

public interface IList {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 clear();public void display();
}

再次回顾:接口中的方法默认都是 public abstract 修饰的,这里不写public也可以

正式使用LinkList 之前还是一样 自己实现一个链表 方便我们更好的理解链表

2. MySingleList 类中具体实现(不带头单向非循环链表)

1. 节点抽象成内部类

在这里插入图片描述

手搓一个链表

就是

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;}
2. 头插法

在这里插入图片描述
在这里插入图片描述

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

在这里插入图片描述
在这里插入图片描述

public void addLast(int data) {ListNode node  = new ListNode(data);if(head==null){node=head;return ;}ListNode cur  =head;while(cur.next!=null){cur=cur.next;}cur.next=node;}
4. 指定位置插入

首先要判断插入的位置是否合法 , 写一个自定义异常

public class IndexException extends RuntimeException{public IndexException(){}public IndexException(String message){super(message);}
}

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

public void addIndex(int index, int data) {if(index==0){addFirst(data);return ;}int len=size();//获取链表长度if(index==len){addLast(data);}ListNode node=new ListNode(data);ListNode cur=head;try{while(index-1!=0){cur=cur.next;index--;}node.next=cur.next;cur.next=node;}catch (IndexException e){System.out.println("index位置不合法!!!!");e.printStackTrace();}}
5. 是否包含key值

在这里插入图片描述

public boolean contains(int key) {//还是需要cur 不能让头结点移动,要不然找不到头了ListNode cur=head;while (cur!=null){if(cur.val==key){return true;}cur=cur.next;}return false;}
6. 删除指定值的节点

在这里插入图片描述

在这里插入图片描述

public void remove(int key) {ListNode node=new ListNode(key);if(head==null){//空链表  直接return  没啥可删的return;}if(head.val==key){//如果要删的是头  把头定位下一个节点就行了head=head.next;return ;}ListNode cur = findNodeOfKey(key);if(cur==null){return; //没找到要删除的值}ListNode del = cur.next;//要是找到了,就架空del  删除delcur.next=del.next;}public ListNode findNodeOfKey(int key){ListNode cur=head;while (cur.next!=null){if(cur.next.val==key){return cur;}cur=cur.next;}return null;}
7. 删除所有指定值所在节点

在这里插入图片描述

在这里插入图片描述

public void removeAllKey(int key) {if(head == null) {  //头为空 直接返回return;}ListNode prev = head;ListNode cur = head.next;//cur 代表当前节点是否为你要删除的节点while (cur != null) {if(cur.val == key) { //判断一下是否为指定值prev.next = cur.next;cur = cur.next;//  是指定值就 架空cur 删除cur  并且cur向后走一位}else {prev = cur;cur = cur.next;  //不是指定值就  两个都往后走}}if(head.val == key) {head = head.next;   //头为指定值  删头}}
8. 获取链表长度

在这里插入图片描述

 public int size() {int len = 0;ListNode cur = head;while (cur != null) {len++;cur = cur.next;}return len;}
9. 清空链表

两种方法: ①直接head置为空 ,有点暴力
②head cur 一个一个置为空, 更温和一点

在这里插入图片描述

public void clear() {ListNode cur = head;while (cur != null) {ListNode curN = cur.next;cur.next = null;cur = curN;}head = null;}
10. 打印链表
 public void display() {ListNode cur = head;while (cur != null) {System.out.print(cur.val+" ");cur = cur.next;}System.out.println();}
}

三. 链表面试题

关于链表的面试题 都在刷题专栏 test1,像练习链表题的同学可以移步刷题专栏的第一篇文章

以上我们讲的都是不带头单向非循环链表

集合类中都是双向链表,大家注意区分,内部类的实现有所区别,双向链表有三个域,前驱域,数据域,后驱域

四. MyLinkedList 的模拟实现(不带头双向非循环链表)

节点抽象成内部类

static class ListNode{public int val;public ListNode prev;public ListNode next;public ListNode(int val) {this.val=val;}}public  ListNode head;public  ListNode last;

1.头插法

在这里插入图片描述

在这里插入图片描述

2.尾插法

 public void addLast(int data) {
//尾插 遍历整个链表 最后一个节点的next 指向nodeListNode node=new ListNode(data);if(head==null){head=node;return ;}ListNode cur=head;while(cur.next!=null){cur=cur.next;}cur.next=node;}

3.指定位置插入

 public  void addIndex(int index, int data) {int len=size();if(index<0 || index>len){System.out.println("index位置不合法");return;}if(index==0){addFirst(data);return ;}if(index==len){addLast(data);return ;}ListNode cur=head;while(index-1!=0){cur=cur.next;index--;}ListNode node=new ListNode(data);node.next=cur.next;cur.next=node;}

4.是否包含Key值

public boolean contains(int key) {ListNode cur=head;while(cur!=null){if(cur.val==key){return true;}cur=cur.next;}return false;}

5. 删除指定值的节点

public void remove(int key) {if(head==null){return ;}//头是需要删除的值 删除 头if(head.val==key){head=head.next;return ;}ListNode cur=findKeyNode(key);ListNode del=cur.next;//cur是需要删除的节点的前一个if(cur==null){return ;//没有需要删除的节点}cur.next=del.next;//架空删除 哪个需要删除的节点}private ListNode findKeyNode(int key){ListNode cur=head;while(cur.next!=null){if(cur.next.val==key){return cur; //获得需要删除的节点的前一个结点  单链表走过了 无法找到前一个结点}cur=cur.next;}return null;}

6.删除所有指定值的节点

public void removeAllKey(int key) {if(head==null){return ;}if(head.val == key) {head = head.next;}ListNode prev=head;ListNode cur=head.next;while(cur!=null){if(cur.val==key){prev.next=cur.next;cur=cur.next;}else{prev=cur;cur=cur.next;}}}

7. 获取链表长度

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

8. 清空链表

 public void clear() {//不是把头置为空  太暴力了 我们一个一个置空ListNode cur=head;while(cur!=null) {ListNode curN = cur.next;cur.next  = null;cur = curN;}head=null;}

9.打印链表

public void display() {//打印ListNode cur=head;while(cur!=null){System.out.print(cur.val+" ");cur=cur.next;}System.out.println();}

五. LinkedList 的使用

5.1 什么是LinkedList

LinkList官方文档

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

在这里插入图片描述

在集合框架中,LinkedList也实现了List接口,具体如下:
在这里插入图片描述

【说明】

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

5.2 LinkedList 的使用

5.2.1 LinkedList 的构造

在这里插入图片描述

 public static void main(String[] args) {// 构造一个空的LinkedListList<Integer> list1 = new LinkedList<>();//无参构造List<String> list2 = new java.util.ArrayList<>();list2.add("JavaSE");list2.add("JavaWeb");list2.add("JavaEE");// 使用ArrayList构造LinkedListList<String> list3 = new LinkedList<>(list2);// 使用集合容器中的元素构造}

通配符 (讲解ArrayList的博客中有介绍)list2 由List创建 属于LinkedList 的子类所以可以作为参数

5.2.2 LinkedList的其他常用方法介绍

在这里插入图片描述

简单演示几个方法 大家也可以自己点击源码查看其他方法的逻辑
在这里插入图片描述

5.3 LinkedList的遍历

5.3.1 for遍历

就是MyLinkedList里的display()方法

5.3.2 foreach

 LinkedList<Integer> list = new LinkedList<>();list.add(1);   // add(): 默认尾插list.add(2);list.add(3);list.add(4);list.add(5);list.add(6);list.add(7);System.out.println(list.size());// foreach遍历for (int e:list) {System.out.print(e + " ");}System.out.println();

5.3.3 迭代器遍历(正向反向)

 // 使用迭代器遍历---正向遍历ListIterator<Integer> it = list.listIterator();while(it.hasNext()){System.out.print(it.next()+ " ");}System.out.println();// 使用反向迭代器---反向遍历ListIterator<Integer> rit = list.listIterator(list.size());while (rit.hasPrevious()){System.out.print(rit.previous() +" ");}System.out.println();

六. ArrayList和LinkedList的区别

在这里插入图片描述

相关文章:

  • chili3d调试笔记3 加入c++ 大模型对话方法 cmakelists精读
  • 学习海康VisionMaster之中线查找
  • 力扣每日打卡 2176. 统计数组中相等且可以被整除的数对(简单)
  • Docker使用、容器迁移
  • Vue实现版本检测与升级
  • 软件开发中的入静与禅定:探寻深度专注与灵感的源泉
  • 【人力资源管理系统】C#实现
  • Linux之基础命令
  • 笔记整理四
  • Java基础 4.18
  • ctfshow-大赛原题-web702
  • 基于WOA鲸鱼优化的NARMAX模型参数辨识算法MATLAB仿真,对比PSO优化算法
  • 云计算(Cloud Computing)概述——从AWS开始
  • 守护进程编程
  • 音视频之H.265/HEVC变换编码
  • kafka jdbc connector适配kadb数据实时同步
  • Uniapp调用native.js使用经典蓝牙串口通讯方法及问题解决
  • Web 前端包管理工具深度解析:npm、yarn、pnpm 全面对比与实战建议
  • 第五章 SQLite数据库:4、SQLite 进阶用法:常见的约束、PRAGMA 配置、数据操作
  • 微信小程序怎么分包步骤(包括怎么主包跳转到分包)
  • 教育强国建设基础教育综合改革试点来了!改什么?怎么改?
  • “一对一讨论诸多事宜”,泽连斯基披露此次特泽会更多细节
  • 苏迪曼杯即将在厦门打响,国羽向创纪录的14冠进军
  • 蜀道考古调查阶段性成果发布,新发现文物遗存297处
  • 李彦宏:DeepSeek不是万能,多模态将是未来基础模型的标配
  • 沈阳市委常委马原出任阜新市委副书记、市政府党组书记