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

数据结构:链表

 链表的概念及结构:

链表的概念:

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

那物理存储结构连续是什么意思?

之前我们讲过顺序表,顺序表的底层是数组,如下:

因为数组开辟的空间,是连续的,这里所储存的数据,它们在物理储存结构上是连续的

链表的结构:

链表是一个节点链接另一个节点的,一个节点都是一个对象。

如果我用链表储存11,22,33,44,55这几个数据,首先我们有五个节点,因为节点是对象,所以会分配地址。

并把这些数据储存到对应的节点里面,然后再将他们连接起来。

 这个链表叫做单向不带头非循环链表

 这是单向带头非循环链表,两个的区别是,带头链表多了一个头(也叫哨兵位),头插一个元素时,带头链表会将新的元素放在头(哨兵位)的后面。带头链表的值没有意义,所以也可以不用放数据。但是不带头链表,头插的时候,头插在第一个元素的前面即可。

链表分类:

 这就是循环链表,最后一个节点又指向第一个节点,形成一个圈。

单向不带头非循环链表的实现:

我先创建一个内部类(节点):

static class ListNode(){int val;Listnode next;
//构造方法public ListNode(int val){this.val=val;}
}
//还要有一个头结点
ListNode head=null;

在ListNode类中,val用来存储数据,而next用来指向下一个节点,再写一个构造方法用来初始化val值。

定义一个头节点可以利于遍历链表。

实现链表dispaly(打印链表):

如何打印上面链表?我们要打印出每个节点的val值,并利用每个节点的next找到下一个节点。

一直这样循环,一直到最后一个节点的next值为null停止。

public void dispaly(){ListNode cur=head;while(next!=null){System.out.print(cur.val+" ");cur=cur.next;}
}

这里要注意一点:不要直接拿头结点head去遍历链表,会导致最后head指向链表的尾部,后续操作就不方便了。所以重新定义一个cur节点等于头结点。用cur来遍历数组。

实现链表addFirst(头插):

 在实现链表头插时要分情况:

1.如果链表为空:

这种情况,只用定义新的节点,让头结点指向新节点

public void addFirst(int data){
//链表为空的情况if(head==null){ListNode node=new ListNode(data);head=node;}
}

2、如果链表不为空:

 这种情况怎么头插呢?

首先:我们还是要第一个新的节点,让这个新的节点的next值指向head头节点,最后将head头节点指向新的头结点。

public void addFirst(int data){
//链表为空的情况if(head==null){ListNode node=new ListNode(data);head=node;
//链表不为空的情况}else{ListNode node=new ListNode(data);node.next=head;head=node;}
}

最后发现,这两种情况可以合并为:

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

实现链表addLast(尾插):

实现尾插时也要分情况

1.如果链表不为空:

 如果对这个链表进行尾插,应该怎么做呢?

首先我们要遍历链表到这个链表的尾部,然后将要插入的节点与尾节点连接,完成尾插。

public void addLast(int data){
//创建一个新的节点ListNode node=new ListNode(data);ListNode cur=head;
//遍历到链表尾部while(cur.next!=null){cur=cur.next;}
//插入尾部cur.next=node;
}

 2.如果链表为空:

如果用上面1的方法,会出现空指针异常。

所以如果为空链表,我们就直接将head头节点指向需要插入的节点,加一个if语句进行判断。

public void addLast(int data){ListNode node=new ListNode(data);
//为空链表的情况if(head==null){head=node;}
//不为空链表的情况ListNode cur=head;while(cur.next!=null){cur=cur.next;}cur.next=node;
}

实现链表size(求链表长度):

既然要求链表的长度,并定义一个计数器,遍历链表时,只要当前节点不为null,计数器就加1

注意:这里遍历链表时,我们不用head,重新定义一个节点指向head,然后往后遍历。

public int size(){
int count=0;//计数器
ListNode node=head;
//开始遍历数组while(node!=null){count++;node=node.next;}return count;
}

实现链表addIndex(指定位置下插入数据):

既然要在指定位置下插入数据,我们首先就要考虑所提供的下标是否越界:

所以Index(下标)不能小于0,也不能大于链表的长度。

然后如果Index==0,则直接调用头插的方法,如果index==链表的长度,就直接调用尾插的方法。

这里有一个新节点ListNode node =new ListNode(25),要把新节点25插入到22和33之间,应该怎么做呢?

这里我们重新定义一个cur=head;

将cur遍历到22节点的位置

 因为我们要将25这个节点插入2下标的位置,所以要将cur从head移动2-1个位置到22这个位置(这样才方便插入)

node.next=cur.next;

cur.next=node;

public void addIndex(int Index,int data){
int len=size();//求链表的长度 
//判断Index下标是否异常 if(Index<0||Index>len){System.out.print("下标异常");return ;}
//特殊处理Index==0或者Index==len
if(Index==0){addFirst(data);}
if(Index==len){addLast(data);}
//插入到链表的中间下标
ListNode cur=head;
ListNode node=new ListNode(data);
//找到插入下标的前一个位置
while(Index-1!=0){cur=cur.next;
Index--;}
//开始插入
node.next=cur.next;
cur.next=node;
}

`实现链表remove(删除指定节点):

给一个链表,我们如果要删除33这个节点,应该怎么删除呢?

首先,如果这个节点为空,我们直接提前返回。

我们如果要删除33这个节点,那我们要重新定义一个节点,让这个节点指向被删除的节点的前一个节点, 也就是22这个节点。怎么找呢?

先有一个大循环,遍历链表,直到链表为空,然后嵌套一个if,找到被删除节点的前一个节点。

//这个函数用来删除节点
public void remove(int key){if(head==null){return ;}
//如果第一个节点是要删除的节点if(head.val==key){head=head.next;}
//第三种情况
ListNode node=findKey(key);//这时node节点就是要删除的节点的前一个节点
node.next=node.next.next;//删除key节点
}
//这个函数用来找到被删除节点的前一个节点public ListNode findKey(int key){ListNode cur=head;
//遍历链表
while(cur!=null){if(cur.next.val==key){return cur;}cur=cur.next;}
return null;
}

实现链表clear(清空链表):

清空链表有两种方法:

1.最简单的方法:将链表的头节点置为空。

public void clear(ListNode head){head=null;
}

2.第二种方法:遍历链表,将链表的所有的节点全部置为空。

public void clear(ListNode head){
ListNode cur=head;while(cur!=null){ListNode curN=cur.next;//curN指向后一个节点cur.next=null;//将当前的cur节点的next域置为nullcur=curN;//将cur移到curN的位置}
}

相关文章:

  • Vue中如何优雅地处理 `<el-dialog>` 的关闭事件
  • 【MQ篇】RabbitMQ之简单模式!
  • 第T9周:猫狗识别2
  • 机器学习基础 - 分类模型之逻辑回归
  • Linux kernel signal原理(下)- aarch64架构sigreturn流程
  • XHTMLConverter把docx转换html报java.lang.NullPointerException异常
  • 基于SpringBoot的校园赛事直播管理系统-项目分享
  • 前端如何优雅地对接后端
  • 使用Selenium进行元素定位的全面指南
  • docker容器中uv的使用
  • 前端性能优化全攻略:JavaScript 优化、DOM 操作、内存管理、资源压缩与合并、构建工具及性能监控
  • 从代码学习深度学习 - 异步计算 PyTorch 版
  • 深入理解无监督学习:探索数据的潜在结构
  • Android Studio调试中的坑二
  • Idea创建项目的搭建方式
  • this._uid:Vue 内部为每个组件实例分配的唯一 ID
  • 数字化转型“变形记”:中钧科技经营帮如何让企业长出“智慧骨骼”
  • Python简介
  • 搜索策略的基本概念
  • 【音视频】AAC-ADTS分析
  • 马上评丨超常设置战略急需专业,意味着什么
  • 中国英国商会政府事务主席陶克瑞:重庆经济成就瞩目,中英合作机遇无限
  • 普京签署有关批准俄罗斯与伊朗全面战略伙伴关系条约的联邦法令
  • 同济研究生开发AI二维码拿下大奖,新一代00开发者掀起AI创业潮
  • 习近平向加蓬当选总统恩圭马致贺电
  • 从南宋遗韵到海派风情,解码江南服饰美学基因