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

数据结构——LinkedList和链表

目录

一:ArrayList的缺陷

二:链表

2.1 :链表的概念及结构

2.2:链表的实现

三:LinkedList模拟实现

四:总结


一:ArrayList的缺陷

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

二:链表

2.1 :链表的概念及结构

链表是⼀种物理存储结构上⾮连续存储结构,数据元素的逻辑顺序是通过链表中的引⽤链接次序实现的 。
白话一点:
链表不要求连续的内存空间(离散的结构)
元素和元素之间,内存是不连续的,并且这些元素的空间是没啥规律的(顺序上没有要求,内存上也没有要求)
如何知道链表中包含哪些元素、如何遍历链表中所以元素?
  此处就把每个节点上面都引入一个引用变量,称为next
   使用直观引用保存下一个元素对应的内存空间。

就是在a中存放一个next如何存放b的地址,以此类推,到d中就存放一个null,后面没有元素了。

  链表的特点:
  1.链表的元素在离散的内存空间上
  2.每个元素中记录下一个元素地址(引用)
  需要知道第一个元素是谁,后续整个链表就能拿到了

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

1:单向或者双向

单链表只能指知道下一个,不知道上一个
双向链表,每个节点包含两个引用,prev、next(前一个元素地址、后一个元素地址)功能多了,但是消耗的空间更多了

 2.带头或者不带头(是否带有“傀儡节点”)

头节点:应该是链表第一个节点
  傀儡节点:这个节点不存储数据,而是占位置,来简化后续代码

  其实带头也有傀儡节点,只是不显示,如图带头,其实head的next指向傀儡节点,但是傀儡节点不存储数据,那么head的next就指向d1

  3.循环或者⾮循环

重点关注:
⽆头单向⾮循环链表:结构简单,⼀般不会单独⽤来存数据。实际中更多是作为其他数据结构的⼦结构,如哈希桶、图的邻接表等等。另外这种结构在笔试⾯试中出现很多。
⽆头双向链表:在Java的集合框架库中LinkedList底层实现就是⽆头双向循环链表

在java标准库中,LinkedList就是现成的实现,都是实现List的接口。

2.2:链表的实现

package linkedlist;  import java.util.LinkedList;  public class Test1 {  public static void main(String[] args) {  LinkedList<Integer> list = new LinkedList<>();  list.add(1);  list.add(2);  list.add(3);  list.add(4);  list.add(0,5);  list.addFirst(6);  System.out.println(list);//[6, 5, 1, 2, 3, 4]  list.remove(2);// 删除索引为2的元素 1        System.out.println(list);//[6, 5, 2, 3, 4]  list.remove(Integer.valueOf(2));//删除值为2的元素 2        System.out.println(list);//[6, 5, 3, 4]  //头删  list.removeFirst();  System.out.println(list);//[5, 3, 4]  //尾删  list.removeLast();  System.out.println(list);//[5, 3]  list.add(1);  list.add(2);  list.add(3);  list.add(4);  list.add(5);  //通过get方法获取元素  System.out.println(list.get(2));//3  //通过set方法修改元素  list.set(2, 10);  System.out.println(list);//[1, 2, 10, 4, 1, 2, 3, 4, 5]  //通过contians方法判断是否包含元素  list.add(1);  list.add(2);  list.add(3);  list.add(4);  list.add(5);  System.out.println(list.contains(3));//true  System.out.println(list.contains(6));//false  }  
}

三:LinkedList模拟实现

以下代码模拟实现都有注释,如果有不理解的可以评论O~~~

package linkedlist;  
//表示链表的节点  
class Node {  // 节点保存值  public String value;  // 指向下一个节点  public Node next;  public Node(String value) {  this.value = value;  this.next = null;  }  
}  public class MyLinkedList {  //把链表的头节点表示出来,此时整个链表就能获取了  //此时不包含傀儡节点,head==null的时候表示空链表  private Node head=null;  //不像顺序表使用size表示区间的长度,链表使用length表示链表的长度  //但也可以使用size表示个数。  //插入元素  //1:尾插(先找到尾巴,再插入)  public void addlast(String value){  //如果链表为空  if(head==null){  //新建一个节点  Node newNode=new Node(value);  //把新节点放到头部  head=newNode;  return;        }  //先找道尾巴,把新的节点加道尾巴后面  //先创建一个头节点  Node tail=head;  for(;tail.next!=null;tail=tail.next){  if(tail.next==null){  break;  }  }//找到尾巴  //新建一个节点  Node newNode=new Node(value);  tail.next=newNode;  newNode.next=null;  }  //2:头插(  public void addfirst(String value) {  //新建一个节点  Node newNode = new Node(value);  //把新节点放到头部  //1:就要将newNode的next指向head指向的节点  newNode.next = head;  //2:然后我们的head在指向(新节点)newNode  head = newNode;  }  //3:指定位置插入  //在链表中没有下标的概念  //链表需要遍历,找位置。  //但是java标准库,LinkedList同样引入下标这个概念,使用List统一作为ArrayList和LinkedList的接口  public void add(int index,String value) {  //先判断index是否合法  if (index < 0 || index > size()) {//index==size等于尾插,没必要判断  throw new IndexOutOfBoundsException("Index is out of bounds");  }  //针对头插出现的情况  if(index==0){  addfirst(value);  return;        }  //根据当前value值,创建新的节点  Node cur = new Node(value);  //找到index位置的前一个节点  //由于当前链表是单向链表,每个节点稚只能找到next节点  //需要修改前一个节点next的值,让它指向当前节点  //插入新节点,需要找到index-1位置的节点  Node prev = head;  for(int i=0;i<index-1;i++){  prev=prev.next;  }  cur.next=prev.next;  prev.next=cur;  }  //虽然没有size()方法,我们可以自己写一个,遍历链表,计算长度  public int size() {  int size = 0;  for(Node cur =head;cur!=null;cur=cur.next){  size++;  }  return size;  }  //判断某个元素是否存在链表中  public Boolean contains(String value){  //遍历链表,判断是否有value  for(Node cur=head;cur!=null;cur=cur.next){  if(cur.value.equals(value)){  return true;  }  }  return false;  }  //indexOf方法,查找某个元素的位置  public int indexOf(String value){  int index=0;  for(Node cur=head;cur!=null;cur=cur.next){  //每次判断值是否相等,找到就返回index,没有找到就继续遍历index++  if(cur.value.equals(value)){  return index;  }  index++;  }  return -1;  }  //remove方法,删除列表中的元素,根据下标删除  //根据下标删除,需要先找到index位置的前一个节点,然后将前一个节点的next指向index+1位置的节点  public void remove(int index){  if(index<0||index >= size()){  throw new IndexOutOfBoundsException("Index is out of bounds");  }  //特殊处理index为0的情况  if(index==0){  head=head.next;  return;        }  //找到被删除元素前一个节点位置  Node prev=head;  for(int i=0;i<index-1;i++) {  prev = prev.next;  }  //循环结束,prev就指向待删除元素的前一个节点  Node delNode=prev.next;  //将prev的next指向待删除元素的下一个节点  prev.next=delNode.next;  delNode.next=null;  }  //remove方法,删除列表中的元素,根据值删除  //根据值删除,需要先找到值所在的节点,然后将前一个节点的next指向index+1位置的节点  public void remove(String value){  //判读链表是否为空  if(head==null){  return;  }  //要删除的元素是头节点  if(head.value.equals(value)){  //直接将头节点指向头节点的下一个节点  head=head.next;  return;        }  //先找到值所在的节点  Node prev=head;  for(;prev!=null;prev=prev.next){  if(prev.next!=null&&prev.next.value.equals(value)){  break;  }  }  //循环结束,prev就指向待删除元素的前一个节点  if(prev==null){  return ;  }  //将prev的next指向待删除元素的下一个节点  Node delNode=prev.next;  //将prev的next指向待删除元素的下一个节点  prev.next=delNode.next;  delNode.next=null;  }  public void clear() {  //清空链表  //直接没有指向头节点的指针,就表示链表为空  head=null;  }  @Override  public String toString() {  //通过这个方法,遍历链表,构成一个字符串,方便打印  //遍历的时候,需要从头节点开始,进行一个一个元素打印  StringBuilder stringbuilder=new StringBuilder();  stringbuilder.append("[");  for(Node cur=head;cur!=null;cur=cur.next){  //NOde cur=head : 指向头节点(开始遍历,取头节点)  //cur!=null : 循环条件,当cat不为null的时候,才进行循环  //cur.next : 指向下一个节点(把下一个节点取出来)  stringbuilder.append(cur.value);  //当前我们不希望最后一个元素后面带上逗号,所以这里判断一下  if(cur.next!=null) {  stringbuilder.append(",");  }  }  stringbuilder.append("]");  return stringbuilder.toString();  }  private static void test1(){  MyLinkedList list=new MyLinkedList();  list.addfirst("a");  list.addfirst("b");  list.addfirst("c");  list.addfirst("d");  list.addfirst("e");  //还需要遍历链表才能打印出元素  System.out.println(list.toString());  }  public static void  test2(){  MyLinkedList list=new MyLinkedList();  list.addlast("a");  list.addlast("b");  list.addlast("c");  list.addlast("d");  list.addlast("e");  //还需要遍历链表才能打印出元素  System.out.println(list.toString());  }  public static void  test3(){  MyLinkedList list=new MyLinkedList();  list.add(0,"a");  list.add(1,"b");  list.add(2,"c");  list.add(3,"d");  list.add(4,"e");  list.add(2,"lllllll");  System.out.println(list.toString());  }  public static void  test4(){  MyLinkedList list=new MyLinkedList();  list.add(0,"a");  list.add(1,"b");  list.add(2,"c");  list.add(3,"d");  list.add(4,"e");  System.out.println(list.contains("a"));//true  System.out.println(list.contains("f"));//false  }  public static void  test5(){  MyLinkedList list=new MyLinkedList();  list.add(0,"a");  list.add(1,"b");  list.add(2,"c");  list.add(3,"d");  list.add(4,"e");  System.out.println(list.indexOf("a"));  System.out.println(list.indexOf("f"));  }  public static void  test6(){  MyLinkedList list=new MyLinkedList();  list.add(0,"a");  list.add(1,"b");  list.add(2,"c");  list.add(3,"d");  list.add(4,"e");  list.remove(2);  System.out.println(list.toString());  }  public static void  test7(){  MyLinkedList list=new MyLinkedList();  list.add(0,"a");  list.add(1,"b");  list.add(2,"c");  list.add(3,"d");  list.add(4,"e");  list.remove("c");  System.out.println(list.toString());  }  public static void  test8(){  MyLinkedList list=new MyLinkedList();  list.add(0,"a");  list.add(1,"b");  list.add(2,"c");  list.add(3,"d");  list.add(4,"e");  list.clear();  System.out.println(list.toString());  }  public static void main(String[] args) {  test1();//头插  test2();//尾插  test3();//测试中间元素  test4();//测试元素是否存在  test5();//测试元素的位置  test6();//测试删除元素  test7();//测试根据值删除元素  test8();//测试情况链表为空  }  }

关于clear:
一旦head==null,此时1就没有无人指向
1这个节点,就会被GC给释放掉了
1被释放之后,2就没有人指向了,2也会被GC释放,后面也差不多,依此类推。

四:总结

本篇博客讲述了链表的概念以及使用方法,最后模拟实现了链表。

如果有不明白的可以评论哦。

http://www.dtcms.com/a/427201.html

相关文章:

  • 一学一做专题网站哈尔滨黑大主题邮局
  • 基于类的四种设计模式
  • 用ChatGPT修改论文,如何在提升质量的同时降低AI检测风险?
  • 实验指导-基于阿里云Serverless应用引l擎SAE的服务部署迀移
  • 黔西县住房和城乡建设局网站个人网页制作方法
  • 长沙网站推广系统动态wordpress动态主题
  • 基于Matlab实现路径规划
  • 给定数据规模的ACM风格笔试题-子矩阵的最大累加和问题
  • 一站式服务图片wordpress博客整站源码
  • 明星粉丝网站怎么做建设银行手机银行官方网站下载安装
  • Spring boot中 限制 Mybatis SQL日志的大字段输出
  • SQL Server数据库事务日志问题的诊断与解法(从膨胀到瘦身)
  • Postgresql CLOG文件及其从库同步解析
  • wordpress 授权一个空间两个网站对seo
  • 正规的招聘网站永州市网站建设
  • 加强教育信息网站建设昆山建设工程安监站网站
  • EndoChat:面向内镜手术的基于事实依据的多模态大型语言模型|文献速递-文献分享
  • 零基础学AI大模型之ChatModel聊天模型与ChatPromptTemplate实战
  • 产生式规则对自然语言处理深层语义分析的影响与启示研究
  • web渗透之Python反序列化漏洞
  • 做办公用品网站工作计划黄页网站是什么
  • 论文阅读 (1) :Control Flow Management in Modern GPUs
  • 吉林省软环境建设网站网络营销属于哪个专业
  • iOS 26 系统流畅度检测 从视觉特效到帧率稳定的实战策略
  • 2025云栖大会,机器人商业时代降临
  • C++面向对象编程三大特性之一:多态
  • TapTalk | 圆桌实录:澳门综合度假村敏捷转型之旅,MongoDB + TapData 赋能酒店业卓越实践
  • 机器人市场:犹如一颗深水核弹
  • 用VS做的网站怎么连接数据库深圳人才招聘网官网
  • mysql_query函数:数据库世界的信使