话不多说直接上代码。
public class SingleLinkedListDemo {
public static void main(String[] args) {
//测试
HeroNode node1 = new HeroNode(1,"宋江","及时雨");
HeroNode node2 = new HeroNode(2,"卢俊义","玉麒麟");
HeroNode node3 = new HeroNode(3,"吴用","智多星");
HeroNode node4 = new HeroNode(4,"林冲","豹子头");
//创建链表
SingleLinkedList list = new SingleLinkedList();
//添加
// list.add(node1);
// list.add(node2);
// list.add(node3);
// list.add(node4);
//按照编号添加
list.addByOrder(node1);
list.addByOrder(node4);
list.addByOrder(node3);
list.addByOrder(node2);
//测试getLength()方法
System.out.println("有效的节点长度为:"+getLength(list.getHead()));
//显示链表
list.listNode();
}
//常见面试题
/**
* 1.求链表长度
* @param head 头节点
* @return 链表长度(不包含头节点)
*/
public static int getLength(HeroNode head){
if (head.next == null){ //空链表
return 0;
}
HeroNode current = head.next;
int length = 0;
while(current != null){
length++;
current = current.next;
}
return length;
}
/**2.返回链表中倒数第n个元素
* 思路:①编写方法,接收head节点,和index
* index表示倒数第index个节点
* ②先把链表遍历,得到链表长度
* ③得到length后,从链表第一个开始遍历,遍历(length-index)个,就得到目标节点
* @param head 头节点
* @param index 倒数索引
* @return
*/
public static HeroNode findLastIndexNode(HeroNode head,int index){
//判断链表是否为空
if (head.next == null){
System.out.println("链表为空");
return null; //没找到
}
//遍历得到长度
int length = getLength(head);
//第二次遍历length-index个,就是目标节点
//先校验index的合理性
if (index <= 0 || index > length){
return null;
}
HeroNode current = head.next; //指向第一个节点
for (int i = 0;i<length-index;i++){
current = current.next;
}
return current;
}
/**
* 3.链表反转
* 思路:①先定义一个节点reverseHead = new HeroNode();
* ②从头遍历原来的链表,每遍历一个节点,将其取出,放到reverseHead的最前端
* ③原来链表的head.next = reverseHead.next;
* @param head
*/
public static void reverseList(HeroNode head){
//如果当前链表为空,或者只有一个节点,无需反转
if (head.next == null || head.next.next == null){
return;
}
//定义一个辅助指针,帮助我们遍历原来的链表
HeroNode current = head.next;
HeroNode next = null; //指向当前节点的下一个节点,后边会用
HeroNode reverseHead = new HeroNode(0,"","");
//遍历原来的链表,每遍历一个就放在reverseHead的最前端
while (current != null){
next = current.next; //暂时保存当前节点的下一个节点
current.next = reverseHead.next; //将current节点插入到reverseHead最前端
reverseHead.next = current;
current = next; //指针后移
}
//将head.next指向reverseHead.next,实现反转
head.next = reverseHead.next;
}
/**
* 4.连表的反向输出
* 方式一:先将链表反转,之后遍历链表输出。但是此方式破坏了原来链表的结构
* 方式二:使用栈的先进后出原则实现反向输出
* @param head
*/
public static void reversePrint(HeroNode head){
if (head.next == null){
System.out.println("链表为空");
}
//创建栈对象,将各节点入栈
Stack<HeroNode> stack = new Stack<>();
//遍历入栈
HeroNode current = head.next;
while (current != null){
stack.add(current);
current = current.next;
}
while (stack.size() > 0){
System.out.println(stack.pop());
}
}
//合并两个有序的单链表,合并后依然有序
public static SingleLinkedList mergeList(SingleLinkedList list1,SingleLinkedList list2){
HeroNode head1 = list1.getHead();
HeroNode head2 = list2.getHead();
//创建一个新的单链表用来存放合并后的数据。
SingleLinkedList mergeList = new SingleLinkedList();
HeroNode mergeHead = mergeList.getHead();
if (head1.next == null && head2.next == null){
return null;
}
if (head1.next == null && head2.next != null){
return list2;
}
if (head1.next != null && head2.next == null){
return list1;
}
HeroNode temp1 = head1.next; //指向第一个链表的第一个元素
HeroNode temp2 = head2.next; //指向第二个链表的第一个元素
HeroNode next; //保存当前节点下一节点的临时变量
while(temp1 != null && temp2 != null){
if (temp1.no < temp2.no){
next = temp1.next; //将当前要插入的节点的下一节点的指针给next,后边会用
temp1.next = null; //将当前节点的next域置空,不然其后边的节点也会插入合并后的链表
mergeList.add(temp1);
temp1 = next;
}else if (temp1.no > temp2.no){
next = temp2.next;
temp2.next = null;
mergeList.add(temp2);
temp2 = next;
}else{ //如果两个序号相等,只加入其中一个链表的数据即可
next = temp1.next; //将当前要插入的节点的下一节点的指针给next,后边会用
temp1.next = null; //将当前节点的next域置空,不然其后边的节点也会插入合并后的链表
mergeList.add(temp1);
temp1 = next;
temp2 = temp2.next; //因为只加入一个且两节点的序号相同,所以2表的指针也要后移。
}
}
// 出了循环会有一张表有剩余,处理剩余的节点
if (temp1 != null && temp2 == null){
while(temp1 != null){
next = temp1.next;
temp1.next = null;
mergeList.add(temp1);
temp1 = next;
}
}else if (temp1 == null && temp2 != null){
while (temp2 != null){
next = temp2.next;
temp2.next = null;
mergeList.add(temp2);
temp2 = next;
}
}
return mergeList;
}
}
//定义一个SingleLinkedList来管理节点
class SingleLinkedList{
//初始化一个头节点,不存放具体数据,只指向表头
private HeroNode head = new HeroNode(0,"","");
public HeroNode getHead() {
return head;
}
/*
添加节点到链表
当不考虑编号顺序时:①先找到链表的最后一个节点 ②将最后节点的next域指向新的节点
*/
public void add(HeroNode heroNode){
//因为头节点不能动,所以需要一个辅助变量来遍历
HeroNode temp = head;
//遍历链表,找到最后的节点
while(true){
if (temp.next == null){
break;
}
//如果没到最后就修改指针,指向下一个节点
temp = temp.next;
}
//当退出循环时,说明到了链表的最后节点
//将最后节点的next域修改为要插入的节点即可
temp.next = heroNode;
}
/**
* 需求:按照编号顺序添加
* 思路:①首先找到新节点在链表中的位置(通过辅助变量遍历)
* ②新节点的next域等于临时变量的next域(新节点.next = temp.next)
* ③将temp的next域指向新节点(temp.next = 新节点)
*/
public void addByOrder(HeroNode node){
//头节点不能动,所以还是需要辅助变量来遍历
//因为是单链表,需要找到要插入位置的前一个元素
HeroNode temp = head;
boolean flag = false; //添加的编号是否存在的标志,默认为false
while (true){
if (temp.next == null){ //说明temp已经是链表的最后一个节点
break;
}
if (temp.next.no > node.no){ //说明已经找到插入位置,就在temp之后插入
break;
}else if (temp.next.no == node.no){ //说明要插入的节点已经存在
flag = true;
break;
}
}
if (flag){
System.out.println("要添加的英雄已经存在,添加失败"+ node.no);
}else{
//添加
node.next = temp.next;
temp.next = node;
}
}
//修改元素,按照编号修改
public void update(HeroNode newHeroNode){
if (head.next == null){
System.out.println("链表为空!");
return;
}
//遍历寻找
HeroNode temp = head.next;
boolean flag = false; //标志找到没找到
while (true){
if (temp == null){
break; //遍历完了
}
if (temp.no == newHeroNode.no){
flag = true;
break; //找到了
}
temp = temp.next; //没找到,一直往后遍历
}
if (flag){
temp.name = newHeroNode.name;
temp.nickname = newHeroNode.nickname;
}else{
System.out.println("没有找到编号为"+newHeroNode.no+"的节点,不能修改");
}
}
/**
* 删除节点
* 思路:①因为是单链表,需要先找到待删除节点的前一个节点temp
* ②temp.next = temp.next.next;
* 被删除的节点将不会被引用,被垃圾回收机制回收
* 比较是否是待删除节点的时候是比较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.next; //此轮没找到,进行下一轮遍历
}
if (flag){
temp.next = temp.next.next;
}else{
System.out.println("要删除的节点不存在"+no);
}
}
//显示链表
public void listNode(){
//判断链表是否为空
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.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;
}
@Override
public String toString() {
return "HeroNode[" + "no = " + no + ", name = " + name + ", nickname = " + nickname + ']';
}
}