单链表(LinkedList)的java实现
重点:
1、链表是以节点的方式存储的;
2、每个节点包含data域、next域:指向下一个节点;
3、链表的各个节点不一定是连续存储;
4、链表分带头节点的链表和没有头节点的链表,具体根据实际需求来确定。
示例类型:
使用带head头节点的单链表实现水浒英雄排行榜管理对英雄人物的增删改查操作:
head节点:
1、不存放具体的数据
2、作用仅仅是表示单链表的头。
class HeroNode{
int no ; //编号
String name ; //名字
String nickName ; //绰号
HeroNode next ; //链表next
}
基本方法:
① 创建:
1、先创建一个头节点head , 作用就是表示单链表的头;
2、后面我们每添加一个节点,就直接加入到链表的最后;
② 遍历:
1、通过一个辅助指针,帮助我们遍历整个单链表
① 添加节点:
(1)第一种方法是在添加英雄时,直接添加到链表的尾部;
(2)第二种方法是在添加英雄时,根据排名插入到指定位置;(如果已经有这个排名,则添加失败,给出提示)
② 遍历:
通过一个辅助变量遍历整个链表。
③ 修改节点:
(1)通过遍历节点,找到需要修改的节点;
(2)temp.name = newHeroNode.name ;
temp.nickName = newHeroNode.nickName ;
④ 删除节点:
(1)找到需要删除节点的前一个节点,用辅助指针temp指向;
(2)temp.next = temp.next.next ;
(3)被删除的节点,将不会有任何其他引用指向,而被java垃圾回收机制自动回收。
单链表代码实现:(注释详细)
package LinkedList;
public class SingleLinkedListDemo {
//单链表模拟,这个public类默认是用来测试的
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, "林冲", "豹子头");
//创建一个单链表
SingelLinkedList singelLinkedList = new SingelLinkedList();
//加入元素(第一种方式)
// singelLinkedList.add(hero1);
// singelLinkedList.add(hero2);
// singelLinkedList.add(hero3);
// singelLinkedList.add(hero4);
//加入元素(第二种方式)
singelLinkedList.addByOrder(hero1);
singelLinkedList.addByOrder(hero4);
singelLinkedList.addByOrder(hero3);
singelLinkedList.addByOrder(hero2);
// singelLinkedList.addByOrder(hero2);
//显示一把
singelLinkedList.list();
//测试修改节点的代码
HeroNode newHeroNode = new HeroNode(2,"小卢","玉麒麟~~~");
singelLinkedList.update(newHeroNode);
System.out.println("修改后的链表情况~~~");
singelLinkedList.list();
//测试删除操作
singelLinkedList.del(1);
singelLinkedList.del(4);
singelLinkedList.del(2);
singelLinkedList.del(3);
System.out.println("删除后的链表情况:");
singelLinkedList.list();
}
}
//定义一个SingleLinkedList 用来管理我们的英雄
class SingelLinkedList{
//首先初始化头节点。
//头节点不要动。
private HeroNode head = new HeroNode(0, "", "");//头节点不存放任何数据
//① 添加节点到单链表
/*
* 具体思路:当不考虑编号的顺序时
* 1、找到当前链表的最后节点
* 2、将最后节点的next指向新节点
*/
//第一种添加方式:将新节点直接添加到链表的尾部
public void add(HeroNode heroNode){
//因为头节点不能动,因此我们要一个辅助指针temp
HeroNode temp = head ;
//遍历链表,找到最后
while(true){
//找到链表的最后
if(temp.next == null){
break;
}
//如果没有找到最后,就将temp后移
temp = temp.next;
}
//由上可见,退出while循环时,temp会指向链表的最后
//此时,可以将最后这个节点的next指向新的节点
temp.next = heroNode ;
}
/*
* 第二种添加英雄的方式:
* 要求按照排名书序将英雄插入到指定位置
* 如果排名重复,则添加失败,给出提示
*/
public void addByOrder(HeroNode heroNode){
//因为头节点不能动,所有仍然用一个辅助指针来帮助找到添加元素的位置
//因为是单链表,所以我们要找到插入位置的前一个位置,否则就插入不了。
HeroNode temp = head;
boolean flag = false;//添加的编号是否存在,默认为false
while(true){
if(temp.next == null){//遍历到了链表的最后
break ; //break表示跳出while
}
if(temp.next.no > heroNode.no){
//位置找到,就在temp的后面插入
break;
}
else if(temp.next.no == heroNode.no){
//说明希望的编号已经存在
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;
}
}
//② 修改节点信息,根据no的编号来修改,即no编号不能改。
//思路:
//1、先找到该节点,进行遍历;
//2、temp = name = newHeroNode.name ;
// temp.nickName = newHeroNode.nickName;
//说明:根据newHeroNode的no来修改即可
public void update(HeroNode newHeroNode ){
//判断是否为空
if(head.next == null){
System.out.println("链表为空。");
return ;
}
//找到需要修改的节点,根据no编号找
//定义一个辅助变量
HeroNode temp = head.next ;
boolean flag = false ;
while(true){
if (temp == null){ //链表遍历完毕
break;
}
if(temp.no == newHeroNode.no){
//编号对应上,说明已经找到了需要修改的节点
flag = true ;
break;
}
temp = temp.next;
}
//根据flag判断是否找到要修改的节点
if(flag){
temp.name = newHeroNode.name ;
temp.nickName = newHeroNode.nickName ;
}else{
System.out.printf("没有找到编号为 %d 的节点,无法修改\n" , newHeroNode.no);
}
}
/*
* ③ 从单链表中删除一个节点:
* 1、先找到需要删除的这个节点的第一个节点temp
* 2、temp.next = temp.next.next ;
* 3、被删除的节点,由于没有任何引用指向,在java中成为“垃圾”
* 从而被java的垃圾回收机制自动回收
*/
/*
* 代码思路:
* 1、head不能动,因此需要一个temp辅助节点找到待删除节点的前一个节点
* 2、说明我们在比较时,是temp.next.no 和 需要被删除的节点的no进行比较
*/
public void del(int no){
HeroNode temp = head;
boolean flag = false ; //标识是否找到被删除节点的前一个节点
while(true){
if(temp.next == null){//已经到了链表的最后
break;
}
if(temp.next.no == no){//找到了待删除节点的前一个节点
flag = true ;
break ;
}
temp = temp.next ;
}
//判断flag
if(flag){ //如果flag被修改为true,说明找到了被删除的节点
temp.next = temp.next.next ;
}else{
System.out.println("找不到相应节点,无法被删除!");
}
}
//显示链表元素(遍历链表)
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);//由于toString方法已经重写,所以直接打印temp会很方便
//将temp后移,一定要小心
temp = temp.next;
}
}
}
//定义一个节点类HeroNode
class HeroNode{
public int no ;
public String name ;
public String nickName ;
public HeroNode next ; //指向下一个节点
//构造器
public HeroNode(int hNo , String hname , String hNickname){
this.no = hNo ;
this.name = hname ;
this.nickName = hNickname ;
}
//为了显示方便,我们重写toString方法
@Override
public String toString() {
return "HeroNode [no=" + no + ", name=" + name + ", nickName=" + nickName + "]";
}
}