单链表结点的删除(代码实现)
这里我们以方法的形式呈现单链表删除结点的操作:
/*
单链表结点删除的方法
我们要删除一个节点就要先找到我们的待删除结点的前一个位置,然后让带删除结点的前一个位置直接指向待删除结点
的后一个位置就可以了
*/
public void del(int no){
/*
定义辅助指针(变量), 注意: 由于我们进行删除操作的时候是寻找我们的带删除结点的前一个位置的,
所以我们就要让辅助指针一开始指向头结点
*/
HeroNode temp = head;
//定义一个boolean类型的变量作为标识
boolean flag = false;
//遍历链表,找寻我们想要操作的位置
while(true){
if(temp.next == null){
//表示链表已经遍历到了最后的位置此时的辅助指针指向了链表中的最后一个元素了
//指向了链表的最后一个元素,而辅助变量的含义又是找寻待插入节点的前一个位置,所以这种情况就是表示找不到待插入节点
break;
}
if(temp.next.no == no){
flag = true;
break;
}
temp = temp.next;
}
if(flag){
//表示找到了带插入节点
temp.next = temp.next.next;
}else{
System.out.printf("没有找到编号为%d的结点,不能删除\n",no);
}
}
}
我们将上述单链表结点删除的方法封装到我们的单链表类中:
package com.ffyc.linkedlist;
public class SingleLinkedList {
//先初始化一个头结点,头结点不能动,头结点中不存储具体的数据
/*
头结点一般都是作为我们的链表类中的一个私有属性,我们创建了一个新的单链表其实就是要链表中有一个头结点就可以了
*/
private HeroNode head = new HeroNode(0,"","");
/*
不考虑英雄排名的顺序将我们的结点添加到单向链表中的思路分析:
1. 找到当前这个链表的最后一个结点
2. 将找到的链表中的最后一个节点的指针域指向我们要添加的这个新的节点
*/
public void add(HeroNode heroNode){
//我们往链表中添加元素就要遍历这个链表,那么我们遍历这个链表就要创建一个辅助变量,使用这个辅助变量就可以完成对链表的遍历
HeroNode temp = head;
//遍历链表,找到最后一个节点
while(true){
//找到链表的最后
if(temp.next == null){
break;
}
//如果没有找到最后,那么就将指针后移,继续向后找
temp = temp.next;
}
//当退出while循环的时候,temp就指向了链表中的最后一个元素,所以我们只需要将这个待添加结点添加到这个temp节点的后面就可以了
temp.next = heroNode;
}
/*
遍历链表 --> 也就是显示链表
我们在做遍历操作的时候一定要创建一个辅助变量(指针),这个辅助变量(指针)的作用就是帮助我们来遍历链表
那么我们为什涉及到遍历就要通过一个辅助变量(指针)来完成?
- 因为我们涉及到遍历操作的时候我们就要一个一个节点的去遍历,我们对于遍历的操作肯定是要放到循环中实现,
那么在循环中我们可以通过第一个节点获取到第二个节点,但是当我们使用第一个节点调用next属性获得第二个节
点的时候,这个时候我们就不能获取到第三个结点了,因为我们的获取到的第二个节点都还是临时的,所以我们就要让我们
获取到的第二个节点固定下来,这个时候我们就要使用一个变量来接收这个第二个节点,这样我们的第二个节点就被固
定了下来,那么我们就可以使用这个固定的结点去完成一个循环,一直向下遍历:
- 总结: 其实我们遍历就是从第一个节点走到最后一个节点,那么我们就要使用一个临时变量(或者说辅助变量或者辅助指针)
去和我们所有的结点一个一个的去接触 ---> 就是将一个个的结点都赋给temp变量
*/
public void list(){
//首先我们要判断链表是否为空
if(head.next == null){
System.out.println("链表为空");
return;
}
//因为头结点不能动,所有我们需要一个辅助变量来遍历链表
HeroNode temp = head.next;
while(true){
//判断当亲是否已经到链表的最后
if(temp == null){
break;
}
//输出结点的信息
System.out.println(temp);
//将temp后移
temp = temp.next;
}
}
/*
添加结点的第二种方式
这种方式下添加结点是通过其结点的no属性将其插入到指定的位置上
如果这个排名已经在单链表中存在了,这个时候添加失败并且给出提示
*/
public void addByOrder(HeroNode heroNode){
//因为头结点不能动,所以我们还是通过一个辅助指针来帮助我们找到添加元素的位置
HeroNode temp = head;
/*
注意temp(辅助指针)是从头结点开始的,因为我们可能插入的位置就是在头结点之后的位置
*/
//定义一个boolean型的变量flag , flag标志添加的编号是否已经存在,默认为false,为false就是编号不存在,就是可以添加
boolean flag = false;
while(true){
if(temp.next == null){
//说明temp已经在链表的最后了
break;
}
/*
这个时候一定要使用temp的next的on去和heroNode的no去比较,而不是直接使用temp和heroNode去比较
*/
if(temp.next.no > heroNode.no){
//找到了元素在链表中插入的位置,位置就在temp的后面
break;
}else if(temp.next.no == heroNode.no){
//这个时候就表明我们的链表中已经是存在这个编号的结点了,这个时候我们就不能添加这个节点,那么我们就要将flag设置为true
flag = true;
break;
}
//指针下移
temp = temp.next;
}
//判断flag的值
if(flag){
//这个时候进入到这里就说明flag的值为true,也就是说: 我们的链表中已经有这个编号值了,这个时候添加失败,我们给出提示就可以了
System.out.printf("准备插入的英雄的编号%d已经存在了,不能加入\n",heroNode.no);
}else{
//将节点插入到链表中,temp的后面
heroNode.next = temp.next;
temp.next = heroNode;
}
}
public void update(HeroNode newHeroNode){
//判断链表是否为空
HeroNode temp = head.next;
//定义一个boolean类型的变量表示是否找到了要修改的结点
boolean flag = false;
//遍历链表,遍历完链表之后我们的辅助结点就指向了我们要修改的结点,这个时候我们就可以使用
while(true){
if(temp == null){
//表示已经遍历完链表了
break;
}
if(temp.no == newHeroNode.no){
//表示找到了要修改的结点
flag = true;
break;
}
temp = temp.next;
}
//根据flag判断是否找打了要修改的结点
if(true){
temp.name = newHeroNode.name;
temp.nickname = newHeroNode.nickname;
}else{
System.out.printf("没有找到编号为%d的结点,不能修改\n",newHeroNode.no);
}
}
/*
单链表结点删除的方法
我们要删除一个节点就要先找到我们的待删除结点的前一个位置,然后让带删除结点的前一个位置直接指向待删除结点
的后一个位置就可以了
*/
public void del(int no){
/*
定义辅助指针(变量), 注意: 由于我们进行删除操作的时候是寻找我们的带删除结点的前一个位置的,
所以我们就要让辅助指针一开始指向头结点
*/
HeroNode temp = head;
//定义一个boolean类型的变量作为标识
boolean flag = false;
//遍历链表,找寻我们想要操作的位置
while(true){
if(temp.next == null){
//表示链表已经遍历到了最后的位置此时的辅助指针指向了链表中的最后一个元素了
//指向了链表的最后一个元素,而辅助变量的含义又是找寻待插入节点的前一个位置,所以这种情况就是表示找不到待插入节点
break;
}
if(temp.next.no == no){
flag = true;
break;
}
temp = temp.next;
}
if(flag){
//表示找到了带插入节点
temp.next = temp.next.next;
}else{
System.out.printf("没有找到编号为%d的结点,不能删除\n",no);
}
}
}
/**
* 这个就是我们定义的结点类:
*/
class HeroNode{
public int no; //英雄编号 ---> 也就是英雄排名
public String name; //人物名称
public String nickname; //人物的外号
public HeroNode next; //指针域 --> 指向下一个节点
//节点类的构造器
public HeroNode(int no, String name,String nickname){
this.no = no;
this.name = name;
this.nickname = nickname;
}
//为了方便我们后面进行代码的测试,所以这里我们重写toString()方法,这样我们输出英雄的时候就不会是输出地址的哈希码值了
@Override
public String toString() {
/*
注意: 这里我们重写toString()方法的时候一定不要输出结点的next属性---> 也就是不要输出指针域,
如果我们是用编译环境生成的toString()方法,那么我们一定要注意将输出next属性的语句删掉,不然我们在显示结果的
时候就会显示的有问题,我们显示next属性的时候会将这个节点的所有的后续节点全部输出
*/
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickname='" + nickname + '\'' +
'}';
}
}
- 注意: 这个单链表类中还封装了一些其他的方法
测试代码:
package com.ffyc.linkedlist;
public class SingleLinkedListTest4 {
public static void main(String[] args) {
//先创建一些结点:
HeroNode hero1 = new HeroNode(1,"宋江","及时雨");
HeroNode hero2 = new HeroNode(2,"卢俊义","玉麒麟");
HeroNode hero3 = new HeroNode(3,"吴用","智多星");
HeroNode hero4 = new HeroNode(4,"林冲","豹子头");
//创建一个链表
SingleLinkedList singleLinkedList = new SingleLinkedList();
//向链表中加入元素
singleLinkedList.addByOrder(hero1);
singleLinkedList.addByOrder(hero4);
singleLinkedList.addByOrder(hero3);
singleLinkedList.addByOrder(hero2);
//打印结果(也就是显示结果)
singleLinkedList.list();
//删除单链表中结点的no属性为2的结点
singleLinkedList.del(2);
System.out.println("删除后的结果:");
singleLinkedList.list();
//再次删除链表中结点的no属性为2的结点 ,这个时候之前我们已经将no为2的结点删除了,这个时候就应该提示删除失败
singleLinkedList.del(2);
System.out.println("显示删除后的结果:");
singleLinkedList.list();
}
}