数据结构(三)---链表1--单链表
单链表
手写单链表
(1)先写一个节点类HeroNode
有no、有name、有下一个节点next
//初始化一个单链表节点,定义一个HeroNode,每个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 + '\'' +
'}';
}
}
(2)开始用HeroNode类来创建单链表
1-创建头结点,放在0的位置
2-getHead方法可以让外界获取这个头结点
3-添加节点的方法add:参数是一个要被添加的HeroNode的对象,辅助遍历的节点temp先指向头结点
4-遍历显示节点方法list
5-根据no修改节点方法update
6-删除节点的方法remove
//开始创建单链表,来管理上面的节点
class SingleLinkedList {
//首先,写一个头结点,头结点不要动,头结点动了就找不到了
private HeroNode head = new HeroNode(0, "", "");//只是用来存放头结点,不存放具体的数据
//获取头结点
public HeroNode getHead() {
return head;
}
//(1)
//然后,写添加的方法,方法操作的参数就是节点的对象
//每添加一个节点,必须根据排名no找到排在前后的英雄节点,插在中间
public void add(HeroNode heroNode) {
//一个新节点来了,需要找位置,有几种情况:1-排在最前面,后面有前面没有,2-排在中间,前面有后面也有,3-排在最后,前面有后面没有
//要想找节点位置,就要遍历链表,找出英雄的排名no,然后进行比较
//因为头结点不能动,所以需要一个辅助遍历temp
HeroNode temp = head;
//标识添加的编号是否存在,默认为false不存在,如果存在了就不能再添加了
boolean flag = false;
//遍历链表,找到最后,最后一个节点的next为null
while (true) {
//先判断temp是不是最后一个节点
if (temp.next == null) {
//如果是最后一个,不管找没找到,都要break
break;
}
//第一种情况,插在temp和temp.next中间
if (temp.next.no > heroNode.no) {
//就把新英雄节点放在temp前面,头结点后面
// heroNode.next=temp.next;
// head.next=heroNode;
break;
} else if (temp.next.no == heroNode.no) {
flag = true;//说明编号已经存在
break;
}
/*//第二种情况,当节点的排名小于,而它后面的排名大于,说明找到位置了
if (temp.next.no < heroNode.no && temp.next.next.no>heroNode.no) {
//就把新英雄节点放在中间
heroNode.next=temp.next.next;
temp.next.next=heroNode;
}
//第三种情况,要插入的英雄是最小的,
if (temp.no<heroNode.no && temp.next==null)
{
temp.next=heroNode;
}
//第四种情况,遇到相同排名的了或者超出108了,就要提示出现错误了,并且终止循环
if (temp!=null && temp.no==heroNode.no){
System.out.println("该排名出现重复,请检查");
break;
}*/
//如果当前位置不符合上面情况,就让这个temp后移遍历当前链表(这个temp就像一个游标,从head位置往后滑)
temp = temp.next;
}
// 判断flag的值
//在循环的时候不做操作,而是先取flag值,最后出循环,根据flag把相同的情况一起操作处理
if (flag) {
//如果flage为true,说明已经存在,不能添加
System.out.printf("准备插入的英雄编号%d已经存在,不能重复添加\n", heroNode.no);
} else {
//如果flage为false,说明不存在,可以添加
//只需要讨论两种情况,temp是最后一个,temp不是最后一个且插在中间
heroNode.next = temp.next;
temp.next = heroNode;
}
}
//(2)
//然后,写一个显示方法显示所有节点,也是用到一个辅助游标节点来遍历
public void list() {
//先判断链表是否为空(也就是只有头结点)
if (head.next == null) {
System.out.println("单链表为空,没有数据");
return;
}
//如果链表不为空,就开始遍历
//因为头结点不能动,所以需要一个辅助游标变量和遍历,从头结点后面的第一个节点开始滑
HeroNode temp = head.next;
//
while (true) {
//判断是否到了链表最后
if (temp == null) {
//如果为空,说明到了最后一个节点,就停止下滑
break;
}
//如果不为空,说明还没到最后,那就先输出节点信息,使用类里的toString来输出
System.out.println(temp);
//输出完以后再接着往下滑
temp = temp.next;
}
}
//(3)
//根据排名的编号no,把已有的节点改成新的
public void update(HeroNode newHeroNode) {
//辅助变量
HeroNode temp = head.next;
//标识是否找到目标节点
boolean flag = false;
//先判断链表是不是空的
if (temp == null) {
//如果是空的,就返回空值
System.out.println("链表为空,没有数据");
return;
}
//如果不为空,那就根据no通过遍历找到要修改的节点
//通过while循环和标识flag来实现,while循环只负责找,出了循环再根据flag修改
while (true) {
//先判断一种情况,找到链表最后了也没找到
if (temp == null) {
break;//循环结束
}
//通过判断no是否相等来找到目标节点
if (temp.no == newHeroNode.no) {
// 这里不能先操作,这里只负责找,找到了就修改flag,具体操作出了循环再说
// temp.name=newHeroNode.name;
// temp.nickName=newHeroNode.nickName;
//找到了就把标识改成true
flag = true;
//既然找到了,那就结束循环
break;
}
//如果当前节点不符合上面两种判断,那就后移一位,接着遍历
temp = temp.next;
}
//当结束循环时,flag要么还为false,要么为true
//如果flag为true,就说明找到了,就把新节点的信息赋值给目标节点
if (flag = true) {
temp.name = newHeroNode.name;
temp.nickName = newHeroNode.nickName;
System.out.println("修改完毕");
} else {
//如果没找到,那就提示说没找到
System.out.printf("没有找到编号为%d的英雄节点\n", newHeroNode.no);
}
}
//(4)
//删除目标节点
//先找到待删除节点前面一个节点temp,方法就是temp.next=temp.next.next
public void delete(int no) {
HeroNode temp = head;
boolean flag = false;
if (temp.next == null) {
System.out.println("当前列表是空的,没有数据");
return;
}
while (true) {
if (temp.next == null) {
System.out.println("已经找到最后了也没找到待删除的节点,结束");
break;
}
if (temp.next.no == no) {
System.out.printf("找到编号为%d待删除的节点了\n", temp.next.no);
flag = true;
break;
}
//当前节点也不是最后一个节点,也不是待删除节点,所以接着往下遍历
temp = temp.next;
}
if (flag) {
temp.next = temp.next.next;
System.out.println("删除完毕");
} else {
System.out.printf("没有找到编号为%d的待删除节点\n", no);
}
}
}
(3)测试类
HeroNode hero01 = new HeroNode(1, "宋江", "及时雨");
HeroNode hero02 = new HeroNode(2, "卢俊义", "玉麒麟");
HeroNode hero03 = new HeroNode(3, "吴用", "智多星");
HeroNode hero04 = new HeroNode(4, "林冲", "豹子头");
//然后,把英雄对象添加到单链表里去
SingleLinkedList singleLinkedList = new SingleLinkedList();
singleLinkedList.add(hero03);
singleLinkedList.add(hero01);
singleLinkedList.add(hero02);
singleLinkedList.add(hero04);
几个练习题
1-求单链表中节点的个数(给一个头结点参数,找出后面链表的节点个数)
/**(5)求单链表中节点的个数(给一个头结点参数,找出后面链表的节点个数)
* @MethodName: getLength
* @Author: AllenSun
* @Date: 2019/10/31 20:34
*/
public static int getLength(HeroNode head){
if (head.next==null){
return 0;
}
HeroNode temp=head.next;
int length=0;
while (temp!=null){
length++;
temp=temp.next;
}
return length;
}
2-求单链表倒数第k个节点
/**(6)求单链表倒数第k个节点
* 1-接受head节点,同时接收一个index(倒数第index节点)
* 2-先把链表从头到尾遍历,得到链表的总长度getLength
* 3-得到size后,从链表第一个节点开始遍历,遍历(size-index)个节点,就得到了
* 4-如果找到了,就返回这个节点,如果找不到,就返回空
*
* @MethodName:
* @Author: AllenSun
* @Date: 2019/10/31 0:49
*/
public static HeroNode findLastIndexNode(HeroNode head, int index){
//先判断是不是空链表
if (head.next==null){
System.out.println("链表为空,没有数据");
return null;
}
//如果不为空,那就遍历链表,获取链表的长度
int size=getLength(head);
//知道了长度后,就可以根据节点index来找
//1-索引小于0,说明索引错了
if (index<=0 || index>size){
System.out.println("索引错误,超出范围");
return null;
}
//定义给辅助变量,for循环定位到倒数的index
HeroNode cur=head.next;
for (int i = 0; i < size-index; i++) {
cur=cur.next;
}
return cur;
}
3-单链表的反转
public static void reversetList(HeroNode head){
//如果链表是空的,或者只有一个节点,那么就不需要反转,直接返回就行了
if (head.next==null || head.next.next==null){
return;
}
//先定义一个辅助的指针,用来遍历原始的链表
HeroNode cur=head.next;
//定义一个next指针节点,指向当前节点cur的下一个节点
HeroNode next=null;
HeroNode reverseHead=new HeroNode(0,"","");
//开始遍历原始链表,每遍历一个节点,就把这个节点取出来,放在新的链表reverseHead的最前端(也就是头结点后面)
while (cur!=null){
//如果为空,说明遍历到了最后一个节点
//如果不为空,cur就接着往下遍历,
next=cur.next;//先暂时保存当前节点cur的下一个节点next,因为后面需要使用
cur.next=reverseHead.next;//把cur的下一个节点指向新的链表的最前端
reverseHead.next=cur;
cur=next;//让cur后移到next节点的位置
}
//把head.next指向reverseHead.next,实现单链表的反转
head.next=reverseHead.next;
}
4-从尾到头来打印单链表
/**(8)从尾到头来打印单链表
* 1-方式一:把单链表反转操作,然后遍历(改变了原来链表的结构,不推荐使用)
* 2-方式二:可以使用栈,把各个节点压入到栈中,利用栈的先入后出特点,实现逆序打印的效果
* @MethodName:
* @Author: AllenSun
* @Date: 2019/10/31 22:39
*/
public static void reversePrint(HeroNode head){
if (head.next==null){
return;//空链表,不能打印
}
//创建一个栈,把各个节点压入栈
Stack<HeroNode> stack=new Stack<HeroNode>();
HeroNode cur=head.next;//cur节点用来遍历
//把链表的所有节点利用while循环,挨个把节点压入栈
while (cur!=null){
stack.push(cur);//把当前不为空的节点压入栈
cur=cur.next;//当前节点后移一位
}
//循环把节点全部压入栈后,就可以遍历栈来挨个打印
while (stack.size()>0){
System.out.println(stack.pop());
}
}
单链表插入节点–插在最后
//然后,写添加的方法,方法操作的参数就是节点的对象
//每添加一个节点,就直接加入到链表的最后位置
public void add(HeroNode heroNode) {
//一个新节点来了,首先要找到单链表里最后一个节点,然后让最后节点的next指向这个新节点
//要想找到最后一个节点,就要遍历链表
//因为头结点不能动,所以需要一个辅助遍历temp
HeroNode temp = head;
//遍历链表,找到最后,最后一个节点的next为null
while (true) {
//当节点的next为空时,说明这个就是最后一个节点
if (temp.next == null) {
break;
}
//如果没有找到,就让这个temp后移(这个temp就像一个游标,从head位置往后滑)
temp = temp.next;
//当退出while循环时,temp一定是指向最后一个节点的
}
//找到了最后一个节点,直接把新节点连上就行了
temp.next = heroNode;
}
单链表插入节点–根据排名插入
//然后,写添加的方法,方法操作的参数就是节点的对象
//每添加一个节点,必须根据排名no找到排在前后的英雄节点,插在中间
public void add(HeroNode heroNode) {
//一个新节点来了,需要找位置,有几种情况:1-排在最前面,后面有前面没有,2-排在中间,前面有后面也有,3-排在最后,前面有后面没有
//要想找节点位置,就要遍历链表,找出英雄的排名no,然后进行比较
//因为头结点不能动,所以需要一个辅助遍历temp
HeroNode temp = head;
//标识添加的编号是否存在,默认为false不存在,如果存在了就不能再添加了
boolean flag=false;
//遍历链表,找到最后,最后一个节点的next为null
while (true) {
//先判断temp是不是最后一个节点
if (temp.next==null){
//如果是最后一个,不管找没找到,都要break
break;
}
//第一种情况,插在temp和temp.next中间
if (temp.next.no>heroNode.no){
//就把新英雄节点放在temp前面,头结点后面
// heroNode.next=temp.next;
// head.next=heroNode;
break;
}else if (temp.next.no==heroNode.no){
flag=true;//说明编号已经存在
break;
}
//如果当前位置不符合上面情况,就让这个temp后移遍历当前链表(这个temp就像一个游标,从head位置往后滑)
temp = temp.next;
}
// 判断flag的值
//在循环的时候不做操作,而是先取flag值,最后出循环,根据flag把相同的情况一起操作处理
if (flag){
//如果flage为true,说明已经存在,不能添加
System.out.printf("准备插入的英雄编号%d已经存在,不能重复添加\n",heroNode.no);
} else {
//如果flage为false,说明不存在,可以添加
//只需要讨论两种情况,temp是最后一个,temp不是最后一个且插在中间
heroNode.next=temp.next;
temp.next=heroNode;
}
}
单链表修改节点–根据排名no来修改
//根据排名的编号no,把已有的节点改成新的
public void update(HeroNode newHeroNode){
//辅助变量
HeroNode temp=head.next;
//标识是否找到目标节点
boolean flag=false;
//先判断链表是不是空的
if (temp==null){
//如果是空的,就返回空值
System.out.println("链表为空,没有数据");
return;
}
//如果不为空,那就根据no通过遍历找到要修改的节点
//通过while循环和标识flag来实现,while循环只负责找,出了循环再根据flag修改
while (true){
//先判断一种情况,找到链表最后了也没找到
if (temp==null){
break;//循环结束
}
//通过判断no是否相等来找到目标节点
if (temp.no==newHeroNode.no){
// 这里不能先操作,这里只负责找,找到了就修改flag,具体操作出了循环再说
// temp.name=newHeroNode.name;
// temp.nickName=newHeroNode.nickName;
//找到了就把标识改成true
flag=true;
//既然找到了,那就结束循环
break;
}
//如果当前节点不符合上面两种判断,那就后移一位,接着遍历
temp=temp.next;
}
//当结束循环时,flag要么还为false,要么为true
//如果flag为true,就说明找到了,就把新节点的信息赋值给目标节点
if (flag=true){
temp.name=newHeroNode.name;
temp.nickName=newHeroNode.nickName;
System.out.println("修改完毕");
}else {
//如果没找到,那就提示说没找到
System.out.printf("没有找到编号为%d的英雄节点\n",newHeroNode.no);
}
}