首先建立一个节点对象
代码实现如下:
/定义HeroNode结点,每个HeroNode对象就是一个结点
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() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickname='" + nickname + '\'' +
'}';
}
新建一个单链表来操作我们的节点,包括节点的增删改查
//新建一个单链表来操作结点,管理我们的英雄
class SingleLinkedList {
//先初始化头结点,头结点不要动,不存放任何数据
private HeroNode head = new HeroNode(0, "", "");
//获得头结点
public HeroNode getHead() {
return head;
}
//添加节点到单向链表
//思路,当不考虑编号顺序时
//1. 找到当前链表的最后节点
//2. 将最后这个节点的next 指向 新的节点
public void add(HeroNode heroNode) {
//首先找到最后一个节点,因为头结点不能动所以我们需要一个临时节点
//此时临时节点指向头节点
HeroNode temp = head;
//循环遍历链表找到最后的节点
while (true) {
//找到链表的最后结点
if (temp.next == null) {
break;
}
//如果没有找到最后结点,就将temp后移
temp = temp.next;
}
//当退出while循环时,temp就指向了最后结点
//将最后这个节点的next赋给新节点
temp.next = heroNode;
}
//按照编号顺序添加节点
//(如果有这个排名,则添加失败,并给出提示)
public void addByNo(HeroNode heroNode) {
//因为头节点不能动,因此我们仍然通过一个辅助指针(变量)来帮助找到添加的位置
//因为单链表,因为我们找的temp 是位于 添加位置的前一个节点,否则插入不了
HeroNode temp = head;
//标志变量用于判断是否存在该节点
boolean flag = false;
while (true) {
if (temp.next == null) { //说明temp已经都链表的最后了
break;
}
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) {
System.out.printf("已经存在编号为%d的英雄了,不能继续添加了\n", heroNode.no);
} else {
//插入到链表中,temp的后面
heroNode.next = temp.next;
temp.next = heroNode;
}
}
//根据编号修改节点信息,注意:编号不能修改
//通过编号找到目标节点然后对其信息进行修改
public void update(HeroNode newHeroNode) {
//判断链表是否为空
if (head.next == null) {
System.out.println("链表为空");
return;
}
//定义一个临时指针,指向head.next
HeroNode temp = head.next;
//定义一个标记变量判断是否找到相应节点
boolean flag = false;
while (true) {
if (temp == null) {
//已经遍历完链表了
break;
}
if (temp.no == newHeroNode.no) {
//找到目标节点,flag置为true
flag = true;
break;
}
//向下继续寻找
temp = temp.next;
}
//先判断是否找到节点
if (flag) {
//找到节点,修改信息
temp.name = newHeroNode.name;
temp.nickname = newHeroNode.nickname;
} else {
//未找到相应节点
System.out.printf("没有找到编号为%d的英雄\n", newHeroNode.no);
}
}
//1. head 不能动,因此我们需要一个temp辅助节点找到待删除节点的前一个节点
//2. 说明我们在比较时,是temp.next.no 和 需要删除的节点的no比较
public void delete(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 = temp.next;
}
//首先判断是否找到
if (flag) {
//找到节点,进行删除,将temp.next赋给temp.next.next;
temp.next = temp.next.next;
} else {
System.out.printf("要删除的编号%d不存在\n", no);
}
}
//显示链表[遍历]
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.next;
}
}
}
几个关于单链表的面试题
1.得到单链表的长度
思路分析
这个较简单所以就没有什么分析过程,注意的是如果是带头节点,不需要统计头节点
代码实现
public static int getLength(HeroNode head) {
if (head.next == null) {
//空链表
return 0;
}
//定义一个辅助指针
HeroNode cur = head.next;
//统计长度
int length = 0;
while (cur != null) {
length++;
//继续向下遍历
cur = cur.next;
}
//返回链表长度
return length;
}
2.查找单链表中倒数第k个结点
思路分析
1)该方法接收两个参数,一个是头结点,一个是目标节点的下标index,即倒数第index个节点
2)遍历得到链表的长度
3)得到长度size之后,循环遍历(size-index)个节点,即可得到倒数第index个节点
4)找到了就返回该节点,否则返回null
代码实现
public static HeroNode getLastIndexNode(HeroNode head, int index) {
//如果链表为空返回null
if (head.next == null) {
return null;
}
//第一次遍历得到链表的长度
int size = getLength(head);
//第二次遍历,size-index就是我们的倒数第k个结点
if (index < 0 || index > size) {
//如果输入的小标不符合规矩返回null
return null;
}
//定义一个辅助遍历,for循环找到第index个结点
//例:size=3,寻找倒数第2个结点 index=2 所以该节点是 3-2 = 1 即该节点的下标为1
HeroNode cur = head.next;
for (int i = 0; i < size - index; i++) {
cur = cur.next;
}
return cur;
}
3.链表反转
思路分析
代码实现
//链表反转
public static void reverseList(HeroNode head){
//当链表为空或者仅一个结点时无需反转
if (head.next ==null || head.next.next == null){
return;
}
//定义一个辅助的指针,来帮助我们遍历原来的链表
HeroNode cur = head.next;
HeroNode next = null; //指向当前【cur】节点的下一节点
//初始化reverseList
HeroNode reverseHead = new HeroNode(0,"","");
//遍历原来的链表每遍历一个就取出一个放到reverseHead.next
while (cur != null){
//先暂时保存当前节点的下一个结点
next = cur.next;
cur.next = reverseHead.next; //将cur的节点的下一个结点指向新的链表的最前端
reverseHead.next = cur;
cur = next; //当前节点后移
}
//将head.next指向reverseHead.next实现链表反转
head.next = reverseHead.next;
}
4.逆序打印链表
思路分析
将链表中的元素一个一个取出压入栈中,然后再从栈中取出
代码实现
public static void reversePrint(HeroNode head){
//先判断链表是否为空
if (head.next == null){
return;
}
//定义辅助指针,帮助我们遍历
HeroNode cur = head.next;
//定义栈
Stack<HeroNode> stack = new Stack<HeroNode>();
while (cur !=null){
//放入栈中
stack.push(cur);
//节点后移
cur = cur.next;
}
//打印栈中的元素
while (stack.size()>0){
System.out.println(stack.pop());
}
}