一、单链表思想?
我们的单链表是由每个节点组成,而节点由data域和next域组成,然后通过当前节点指向下一个节点形成的链表。
data域是专门来存放数据的,而next是存放下一个节点的引用的。
data数据域可以设置多个数据,也可以设置一个数据。
next域值存放的是节点的地址引用
在这里我们将节点封装成一个类,而每个实例对象代表一个节点,而每个节点可以设置data和next
二、单链表操作
1.节点类创建
//定义节点
class HeroNode{
//表示data数据域
public int no;
public String name;
public String nickname;
//下一节点引用 这里就是next域了
HeroNode next; //在没有赋值的情况下,初始值为null
public HeroNode(int no,String name,String nickname){
this.name=name;
this.no=no;
this.nickname=nickname;
}
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickname='" + nickname + '\'' +
'}';
}
}
然后再创建一个专门写增删改查的方法和定义头节点的类
class SingleLinkedList{
//初始化头结点 头结点不动 不存放任何具体数据
private HeroNode head=new HeroNode(0,"","");
public HeroNode getHead() {
return head;
}
//添加节点方法
public void addHeroNode(HeroNode heroNode){
//先找到最后为next域null的节点
//head节点不能动
HeroNode temp=head;
//遍历链表 找到最后
while (true){
//找到链表的最后
if(temp.next==null){
break;
}
//没有找到最后 temp后移
temp=temp.next;
}
//当while循环结束时 就指向链表的最后
//最后这个节点指向新的节点
temp.next=heroNode;
}
//显示链表
public void showQueue(){
//判断链表为空
if(head.next==null){
System.out.println("链表为空!");
return;
}
//不为空 头节点不能动 需要辅助变量来遍历
HeroNode temp=head.next;
while (true){
//是否到链表最后
if(temp==null){
break;
}
System.out.println(temp);
//将next后移
temp=temp.next;
}
}
//按照顺序插入
public void insetNodeByOrder(HeroNode heroNode){
//因为头节点不能动 任然需要一个辅助指针找到添加的位置
HeroNode temp=head;
//因为是单链表 我们找的temp是添加位置的前一个节点否则加入不了
boolean flag=false; //标志添加的标号是否存在
while (true){
if(temp.next==null){//在链表的最后
break;
}
if (temp.next.no>heroNode.no){//位置找到了 就在temp的后面插入
break;
}else if(temp.next.no==heroNode.no){//希望添加的heroNode已经存在 不能添加
flag=true; //编号存在
break;
}
temp=temp.next;
}
if(flag){//不能添加 说明编号存在
System.out.println("插入的信息已经存在!"+"已经存在的编号为:"+heroNode.no);
}else {
//插入到链表中 temp的后面
heroNode.next=temp.next;
temp.next=heroNode;
}
}
//修改节点的信息 根据no来修改
public void updateNode(HeroNode heroNode){
//辅助节点
HeroNode temp=head;
while (true){
if(temp.next==null){
System.out.println("没有找到该数据喔!");
break;
}
if(temp.next.no==heroNode.no){
temp.next.name=heroNode.name;
temp.next.nickname=heroNode.nickname;
break;
}else{
temp=temp.next;
}
}
}
//删除节点
public void deleteNode(int no){
HeroNode temp=head;
while (true){
if(temp.next==null){
System.out.println("没有找到需删除的数!");
break;
}
if (temp.next.no==no){
temp.next=temp.next.next;
break;
}
temp=temp.next;
}
}
注意这里的头节点是不存放任何数据的节点,它的作用是存放下一个节点的引用(也就是下一个节点的地址)
2.添加节点
//添加节点方法 原理是找到最后一个节点,然后接到最后一个节点上
public void addHeroNode(HeroNode heroNode){
//先找到最后为next域null的节点
//head节点不能动
HeroNode temp=head;
//遍历链表 找到最后
while (true){
//找到链表的最后
if(temp.next==null){
break;
}
//没有找到最后 temp后移
temp=temp.next;
}
//当while循环结束时 就指向链表的最后
//最后这个节点指向新的节点
temp.next=heroNode;
}
这里需要注意了 HeroNode temp=head;有很多友友会有疑惑。
1.为什么要创建一个辅助节点来使用?
因为头节点(head)不能改的,最后的遍历需要从头节点开始
2.还有问题就是temp=head 这样之后,temp地址不断变化为什么head不会变化呢?
一开始的我也对这个问题很有疑惑,但是最后发现,head有自己的地址,temp就相当于head 的一个复制品,不会对head 的内存地址有影响
3.遍历所有节点(显示链表)
//遍历链表
public void showQueue(){
//判断链表为空
if(head.next==null){
System.out.println("链表为空!");
return;
}
//不为空 头节点不能动 需要辅助变量来遍历
HeroNode temp=head.next;
while (true){
//是否到链表最后
if(temp==null){
break;
}
System.out.println(temp); //节点类重写了toString()方法可以直接输出
//将next后移
temp=temp.next;
}
}
4.修改节点
//修改节点的信息 根据no来修改
public void updateNode(HeroNode heroNode){
//辅助节点
HeroNode temp=head;
while (true){
if(temp.next==null){
System.out.println("没有找到该数据喔!");
break;
}
if(temp.next.no==heroNode.no){
temp.next.name=heroNode.name;
temp.next.nickname=heroNode.nickname;
break;
}else{
temp=temp.next;
}
}
}
、
HeroNode temp=head; 我们这里的辅助节点是指向head节点的
也就是说head拷贝了一份给temp。(也可以认为temp等于head的拷贝的内容,但是对head的引用没有影响)
我们修改节点主要依靠节点的no属性来找到节点 ,并且修改内容
5.删除节点
//删除节点
public void deleteNode(int no){
HeroNode temp=head;
while (true){
if(temp.next==null){
System.out.println("没有找到需删除的数!");
break;
}
if (temp.next.no==no){
temp.next=temp.next.next;
break;
}
temp=temp.next;
}
}
删除节点的话,我们通过节点属性no来找到对应的节点的前一个节点
删除节为什么要找到对应节点的前一个节点呢?
那么假如我们删除节点2:
能从图里面看出来节点2的next是存放节点3的地址,而节点1的next是存放节点2的节点
所以要删除节点而,就要将节点1指向节点3 也就是将节点3赋值给节点2的next属性
而如果temp找到节点2但是取不到节点1,所以不能完成删除。
三、注意
我们的画图与我定义的节点类的data属性是不一样的,为了更快让大家能够理解单链表的原理。
我画的图使用一个data属性,而我的代码有多个data属性,所以希望大家能理解哈
如果有错误的话,欢迎大家来指正。