实现步骤:
1.首先找到新添加的节点的位置,是通过辅助变量(指针),通过遍历来实现。
2.新的节点 next = temp.next。
3.将temp.next = 新的节点。
package com.linkedlist;
public class demo01 {
public static void main(String[] args) {
//测试
//先创建节点
HeroNode hero1 = new HeroNode(1, "盖伦", "德玛西亚之力");
HeroNode hero2 = new HeroNode(2, "嘉文四世", "德玛西亚皇子");
HeroNode hero3 = new HeroNode(5, "赵信", "德邦总管");
HeroNode hero4 = new HeroNode(4, "艾希", "寒冰射手");
//创建链表
SingleLinkedList sing = new SingleLinkedList();
//加入链表
// sing.add(hero1);
// sing.add(hero2);
// sing.add(hero3);
//用顺序插入的方法来测试
sing.addByOrder(hero4);
sing.addByOrder(hero3);
sing.addByOrder(hero4);
//显示链表
sing.list();
}
}
/*定义SingleLinkedList 来管理我们的角色*/
class SingleLinkedList {
// 先定义一个头及节点,头结点不动不存放具体的数据
private HeroNode head = new HeroNode(0, "", "");
// 添加节点到单项链表
// 思路:当不考虑编号顺序时
// 1:找到当前链表的最后节点
// 2:将最后节点的next指向新加入的节点
public void add(HeroNode heroNode) {
// 因为head头结点不能动,因此我们需要一个辅助遍历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) {
//因为头结点不能动,因此我们仍然通过一个辅助指针来帮助我们寻找节点将要添加的位置
//因为单链表,因为我们找的temp是位于添加位置的前一个节点,否则插不进去
HeroNode temp = head;
boolean flag = false;//flag判断标号是否已存在,默认为false
while (true) {
if (temp.next == null) {//说明该节点要添加到最后
break;
}
if (temp.next.no > heroNode.no) {//位置找到在temp的后面
break;
} else if (temp.next.no == heroNode.no) {//说明编号已存在
flag = true;//将bool值改为true说明编号已存在
break;
}
temp = temp.next;//将节点往后移
}
//判断flag的值为true说明准备插入的值以存在不能插入
if (flag) {//为true说明准备插入的值以存在不能插入
System.out.println("准备插入的英雄编号已存在"+heroNode.no);
return;
} else {
//将新节点指向上一个节点指向的下一个节点
heroNode.next = temp.next;
//将temp的next指向新加入的节点
temp.next = heroNode;
//这样就完成了节点的插入
}
}
// 显示链表【遍历】
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;
}
}
}
/*定义一个HeroNode,每个HeroNode对象就是一个节点*/
class HeroNode {
public int no;
public String name;
public String nickname;
public HeroNode next;//指向下一个节点
// 重写tostring
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickname='" + nickname + '\'' +
'}';
}
//构造器
public HeroNode(int hno, String hname, String hNickname) {
this.name = hname;
this.no = hno;
this.nickname = hNickname;
}
}
测试结果:
准备插入的英雄编号已存在4
HeroNode{no=4, name='艾希', nickname='寒冰射手'}
HeroNode{no=5, name='赵信', nickname='德邦总管'}
以下为对链表的细节优化
加入修改链表节点的方法
/**
* 修改链表数据的方法
* 实现原理按照参数的no与链表的节点no逐个比较
*/
public void updata(HeroNode newheroNode) {
//判断链表是否为空,以头结点的next是否为空
if (head.next == null) {
System.out.println("链表为空");
return;
}
//找到需要修改的节点,根据no来寻找
//定义一个辅助变量
HeroNode temp = head.next;
// 判断是否找到要修改的节点
boolean flag = false;
while (true) {
if (temp.no == newheroNode.no) {
flag = true;
break;
}
if (temp.next == null) {
break;
}
temp = temp.next;
}
if (flag) {
temp.nickname = newheroNode.nickname;
temp.name = newheroNode.name;
System.out.println("已修改");
} else {
System.out.println("未找到" + newheroNode.no + "该节点");
return;
}
}
实现效果:
.加入删除节点的方法
实现思路:
1.先在链表中找的待删除节点的前一个temp
2.将找到的temp,next = temp.next.next
3.被删除的节点不会被其他引用指向,就会被垃圾回收机制清理
代码实现
/**
* 删除链表数据的方法
* 思路:
* 1.head节点不能动,因此还是定义一个temp的辅助节点找到待删除的节点
* 2.用temp.next.no和传入参数的no比较
*/
public void del(int no) {
//判断链表是否为空
if (head.next == null) {
System.out.println("链表为空");
return;
}
//定义辅助节点为了不乱动头结点
HeroNode temp = head;
//判断是否找到待删除节点的变量
boolean flag = false;
while (true) {
if (temp.next.no == no) {
//表示已经找到待删除的节点
flag = true;
break;
}
if (temp.next == null) {//已经到了链表的最后
break;
}
//temp向后移一个节点
temp = temp.next;
}
if (flag) {
//将待删除节点的前一项等于待删除节点的后一项,就完成了删除
temp.next = temp.next.next;
System.out.println("删除成功");
} else {
System.out.println("未找到待删除的节点" + no);
}
}
实现效果:
定义获取链表的节点个数的方法:
实现思路:通过遍历链表来纪录有多少个节点
/**
* 创建获取到单链表的节点的个数(头结点不算)
*/
public static int getLength(HeroNode head){
if(head.next==null){
System.out.println("链表为空");
return 0;
}
int lenght = 0;
//定义一个辅助变量
HeroNode cur = head;
while(cur.next!=null){
lenght++;
cur=cur.next;
}
return lenght;
}
效果展示:
HeroNode{no=1, name='盖伦', nickname='德玛西亚之力'}
HeroNode{no=2, name='嘉文四世', nickname='德玛西亚皇子'}
HeroNode{no=3, name='德莱厄斯', nickname='诺克萨斯之手'}
HeroNode{no=4, name='艾希', nickname='寒冰射手'}
HeroNode{no=5, name='赵信', nickname='德邦总管'}
这个链表有5
查询链表倒数第n个节点
实现思路:
1.编写一个方法接收head节点,同时接收一个index(要寻找的节点位置)
2.index表示倒数第几个的节点
3.先把链表从头遍历到尾,的得到链表的长度(可以用已经写好的方法getLength())
4.得到size后我们从链表的第一个遍历到(size-index)个
5.如果找到就返回找到的节点,没有找到就返回null
/**
* 创建查找链表倒数第n个节点的方法
* 思路:
* 1.编写一个方法接收head节点,同时接收一个index(要寻找的节点位置)
* 2.index表示倒数第几个的节点
* 3.先把链表从头遍历到尾,的得到链表的长度(可以用已经写好的方法getLength())
* 4.得到size后我们从链表的第一个遍历到(size-index)个
* 5.如果找到就返回找到的节点,没有找到就返回null
*/
public static HeroNode findLastIndexNode(HeroNode head,int index){
//判断传入的head是不是为空,为空就返回null
if(head.next==null){
System.out.println("链表为空");
return null;//没有找到
}
//获取到链表长度
int size = getLength(head);
//判断传入的index是否是有效值
if (size<index||index<=0){
System.out.println("请传入正确的值");
return null;
}
//定义一个辅助节点
HeroNode cur = head.next;
//定义一个for循环size-index次得到需要找到的节点
for(int i =0;i<size-index;i++){
cur=cur.next;
}
//返回找到的节点
return cur;
}
效果展示:
//链表的倒数第几个节点
HeroNode a = SingleLinkedList.findLastIndexNode(sing.getHead(),2);
HeroNode{no=1, name='盖伦', nickname='德玛西亚之力'}
HeroNode{no=2, name='嘉文四世', nickname='德玛西亚皇子'}
HeroNode{no=3, name='德莱厄斯', nickname='诺克萨斯之手'}
HeroNode{no=4, name='艾希', nickname='寒冰射手'}
HeroNode{no=5, name='赵信', nickname='德邦总管'}
这个链表有5
艾希
实现将数组反转:
/**
* 将链表反转
*/
public static void reversetList(HeroNode head){
//如果当前链表为空,或者只有一个节点,说明该链表只有0个或1个节点,则不需要反转直接返回
if (head.next==null||head.next.next==null){
System.out.println("链表为空");
return ;
}
//定义一个辅助的指针,帮助我们遍历原来的链表
HeroNode cur = head.next;
//暂时保存当前节点的下一个节点
HeroNode next = null;
//定义一个新的链表来保存已经反转好了的节点
//遍历原来的链表将它们一个个的取出在放到新的链表最前端,
// 最后前面的节点先出到新的节点的位置又被后加入的节点挤到了后面,最后完成了链表的反转
HeroNode reverseHead = new HeroNode(0,"","");
while (cur !=null){
next = cur.next;//先暂时保存当前节点的后一个节点,后面有用
cur.next = reverseHead.next;//将cur的下一个节点指向新链表的最前端
reverseHead.next = cur;//将新节点加入当前节点
cur = next;//让cur后移
}
//将head.next 指向reverseHead.next ,实现链表的反转
head.next = reverseHead.next;
}
效果展示:
HeroNode{no=1, name='盖伦', nickname='德玛西亚之力'}
HeroNode{no=2, name='嘉文四世', nickname='德玛西亚皇子'}
HeroNode{no=3, name='德莱厄斯', nickname='诺克萨斯之手'}
HeroNode{no=4, name='艾希', nickname='寒冰射手'}
HeroNode{no=5, name='赵信', nickname='德邦总管'}
这个链表有5
艾希
HeroNode{no=5, name='赵信', nickname='德邦总管'}
HeroNode{no=4, name='艾希', nickname='寒冰射手'}
HeroNode{no=3, name='德莱厄斯', nickname='诺克萨斯之手'}
HeroNode{no=2, name='嘉文四世', nickname='德玛西亚皇子'}
HeroNode{no=1, name='盖伦', nickname='德玛西亚之力'}
实现链表的逆序打印:
实现思路:
方法1:将链表反转后在打印(这样会破坏链表的结构,不推荐使用)
方法2:利用栈的先入后出的特点,将各个节点压入栈中在打印,从而完成逆序打印
关于栈的小知识:
代码实现:
/**
* 实现链表的逆序打印(用栈的特性)
*/
public static void reversePrint(HeroNode head){
if (head.next == null){
System.out.println("链表为空");
return ;
}
//创建一个栈,将各个节点压入栈
Stack<HeroNode> stack = new Stack<HeroNode>();
//创建辅助节点
HeroNode cur = head.next;
//将链表的所有节点压入栈中
while (cur != null){
stack.push(cur);
//将cur后移
cur = cur.next;
}
//将栈中的节点进行出栈打印(pop出栈)
while (stack.size()>0){
System.out.println(stack.pop());//stack栈的特点就是先入后出
}
}
}
效果展示:
HeroNode{no=5, name='赵信', nickname='德邦总管'}
HeroNode{no=4, name='艾希', nickname='寒冰射手'}
HeroNode{no=3, name='德莱厄斯', nickname='诺克萨斯之手'}
HeroNode{no=2, name='嘉文四世', nickname='德玛西亚皇子'}
HeroNode{no=1, name='盖伦', nickname='德玛西亚之力'}
******************
HeroNode{no=1, name='盖伦', nickname='德玛西亚之力'}
HeroNode{no=2, name='嘉文四世', nickname='德玛西亚皇子'}
HeroNode{no=3, name='德莱厄斯', nickname='诺克萨斯之手'}
HeroNode{no=4, name='艾希', nickname='寒冰射手'}
HeroNode{no=5, name='赵信', nickname='德邦总管'}
实现两个链表的拼接
实现思路:
1.纪录两链表的长度。
2.创建一个for循环循环两个链表长度之和的次数。
3.当否循环变量到第一个链表的长度次数时在将第二个链表的数据插入。
代码实现:
/**
* 实现两链表的拼接
*/
public static void linkSplicing(HeroNode head1, HeroNode head2) {
//先判断传入的两个数组是否有效,不为空
if (head1.next == null || head2.next == null) {
System.out.println("链表为空");
return;
}
//获取两链表的长度
int h1 = getLength(head1);
int h2 = getLength(head2);
//为了不破坏原链表表结构,创建两个辅助节点
HeroNode temp1= head1;
HeroNode temp2= head2;
//for循环,循环两个链表长度之和的次数
for (int i = 0; i < h1 + h2; i++) {
//判断是否遍历完第一个链表
if (i < h1) {
temp1 = temp1.next;
} else {
//确认第一个链表遍历结束后开始往链表一后面加入链表二的数据
temp1.next = temp2.next;
//两链表都向后移
temp2 = temp2.next;
temp1 = temp1.next;
}
}
//将辅助节点连接好的节点赋值给传入的节点,完成连接
head1 = temp1;
}
}
效果展示:
System.out.println("实现链表的拼接");
SingleLinkedList sing2 = new SingleLinkedList();
sing2.add(hero5);
sing2.add(hero7);
System.out.println("打印链表一");
sing.list();
System.out.println("打印链表二");
sing2.list();
System.out.println("********");
SingleLinkedList.linkSplicing(sing.getHead(), sing2.getHead());
sing.list();
实现链表的拼接
打印链表一
HeroNode{no=1, name='盖伦', nickname='德玛西亚之力'}
HeroNode{no=2, name='嘉文四世', nickname='德玛西亚皇子'}
HeroNode{no=3, name='德莱厄斯', nickname='诺克萨斯之手'}
HeroNode{no=4, name='艾希', nickname='寒冰射手'}
HeroNode{no=5, name='赵信', nickname='德邦总管'}
打印链表二
HeroNode{no=6, name='希瓦娜', nickname='龙女'}
HeroNode{no=7, name='雷恩加尔', nickname='傲之追猎者'}
********
HeroNode{no=1, name='盖伦', nickname='德玛西亚之力'}
HeroNode{no=2, name='嘉文四世', nickname='德玛西亚皇子'}
HeroNode{no=3, name='德莱厄斯', nickname='诺克萨斯之手'}
HeroNode{no=4, name='艾希', nickname='寒冰射手'}
HeroNode{no=5, name='赵信', nickname='德邦总管'}
HeroNode{no=6, name='希瓦娜', nickname='龙女'}
HeroNode{no=7, name='雷恩加尔', nickname='傲之追猎者'}