单链表的简单介绍:
链表的链接方向是单向的,对链表的访问要通过顺序读取从头部开始;
链表是使用指针进行构造的列表;又称为结点列表,因为链表是由一个个结点组装起来的;
其中每个结点都有指针成员变量指向列表中的下一个结点;
链表是由结点构成,head指针指向第一个成为表头的结点,而终止于最后一个指向NULL的指针。一般使用时都要定义一个头结点,头结点不存放数据,只是为了方便查找。
本文介绍的链表操作有(定义头结点):
1.单链表的基本操作(增,删,改,查);
2.按顺序插入节点;
3.获取链表节点个数与查找链表中倒数第K个节点;
4.单链表的反转;
5.合并两个链表;
6.双向链表的基本操作(增,删,改,查);
7.环状链表解决约瑟夫问题;
1.单链表的基本操作(增,删,改,查):
代码如下:
package com.mrz.test;
public class SingleLinkedList {
public static void main(String[] args) {
SingleLinkedListDemo singleLinkedListDemo = new SingleLinkedListDemo();
//创建五个节点
HeroNode node1 = new HeroNode(1, "刘备");
HeroNode node2 = new HeroNode(2, "关羽");
HeroNode node3 = new HeroNode(3, "张飞");
HeroNode node4 = new HeroNode(4, "吕布");
HeroNode node5 = new HeroNode(5, "貂蝉");
//添加节点
singleLinkedListDemo.addHeroNode(node1);
singleLinkedListDemo.addHeroNode(node2);
singleLinkedListDemo.addHeroNode(node3);
singleLinkedListDemo.addHeroNode(node4);
singleLinkedListDemo.addHeroNode(node5);
//显示节点
singleLinkedListDemo.showLinkedList(singleLinkedListDemo.getHead());
System.out.println("-----------------------");
//修改貂蝉为西施
singleLinkedListDemo.updataLinkedList(new HeroNode(5, "西施"));
singleLinkedListDemo.showLinkedList(singleLinkedListDemo.getHead());
System.out.println("-----------------------");
//删除节点
singleLinkedListDemo.delLinkedList(5);
singleLinkedListDemo.showLinkedList(singleLinkedListDemo.getHead());
}
}
class SingleLinkedListDemo{
private HeroNode head = new HeroNode(0, "");
//头结点是为了方便查找,此方法用来获取头结点
public HeroNode getHead() {
return head;
}
//添加节点
public void addHeroNode(HeroNode heroNode) {
//因为head节点是不能动的,因此我们需要一个辅助节点temp
HeroNode temp = head;
//添加数据是从链表的最后添加;
//如何确认最后的位置,只能 节点.next 这样遍历下去,直到值为null就找到位置
while(true) {
if(temp.next == null) {
break;
}
temp = temp.next;
}
temp.next = heroNode;
}
//显示链表
public void showLinkedList(HeroNode head) {
//判断链表是否为空
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.刘备 2.关羽 3.张飞
//if(temp.id = hearoNode.id) temp.name = heroNode.name
//修改链表节点数据
public void updataLinkedList(HeroNode heroNode) {
//判断链表是否为空
if(head.next == null) {
System.out.println("链表为空");
return;
}
//定义一个辅助节点
HeroNode temp = head.next;
boolean flag = false;
//是否找到对应节点
while(true) {
//如果找到
if(temp.id == heroNode.id) {
flag = true;
break;
}
//防止空指针报错
if(temp.next == null) {
break;
}
temp = temp.next; //temp后移
}
if(flag) {
temp.name = heroNode.name;
}else {
System.out.println("没有找到id为" + heroNode.id);
}
}
//删除节点
public void delLinkedList(int id) {
HeroNode temp = head;
boolean flag = false;
//找到待删除节点的前一个节点
while(true) {
//已经遍历到最后,需要跳出循环
if(temp.next == null) {
break;
}
//找到
if(temp.next.id == id) {
flag = true;
break;
}
temp = temp.next;
}
if(flag) {
//next是下一个,而next.next就是下下个,实现跳过中间的(删除)
temp.next = temp.next.next;
}else {
System.out.println("没有找到id为" + id);
}
}
}
//相当于一个节点
class HeroNode{
public int id;
public String name;
public HeroNode next; //用来指向下一个节点
public HeroNode(int id, String name) {
super();
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "HeroNode [id=" + id + ", name=" + name + "]";
}
}
2.以上的插入是按顺序插入的,所以输出的也是顺序输出的,但是如果你先插入的是第二条(id为2),再插入第一条(id为1),这时按照上面的方法打印的就是先打印id为2的再打印id为1的,而工作中往往需要插入时能按照顺序插入,那么就有了编号排序插入的方法(在插入时对id做了比较),代码如下:
package com.mrz.test;
public class SingleLinkedList {
public static void main(String[] args) {
SingleLinkedListDemo singleLinkedListDemo = new SingleLinkedListDemo();
//创建五个节点
HeroNode node1 = new HeroNode(1, "刘备");
HeroNode node2 = new HeroNode(2, "关羽");
HeroNode node3 = new HeroNode(3, "张飞");
HeroNode node4 = new HeroNode(4, "吕布");
HeroNode node5 = new HeroNode(5, "貂蝉");
//按顺序添加节点
singleLinkedListDemo.addReorder(node3);
singleLinkedListDemo.addReorder(node1);
singleLinkedListDemo.addReorder(node5);
singleLinkedListDemo.addReorder(node4);
singleLinkedListDemo.addReorder(node2);
//显示节点
singleLinkedListDemo.showLinkedList(singleLinkedListDemo.getHead());
}
}
class SingleLinkedListDemo{
private HeroNode head = new HeroNode(0, "");
//头结点是为了方便查找,此方法用来获取头结点
public HeroNode getHead() {
return head;
}
//显示链表
public void showLinkedList(HeroNode head) {
//判断链表是否为空
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;
}
}
//编号排序插入
public void addReorder(HeroNode heroNode) {
HeroNode temp = head;
boolean flag = false;
while(true) {
//链表到最后
if(temp.next == null) {
flag = true;
break;
}else if (temp.next.id > heroNode.id) {
flag = true;
break;
}else if (temp.next.id == heroNode.id) {
break;
}
temp = temp.next;
}
if(flag) {
//新的节点指向temp指向下一个节点
heroNode.next = temp.next;
//temp节点指向新的节点
temp.next = heroNode;
}else {
//id重复
System.out.println("id" + heroNode.id + "重复");
}
}
}
//相当于一个节点
class HeroNode{
public int id;
public String name;
public HeroNode next; //用来指向下一个节点
public HeroNode(int id, String name) {
super();
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "HeroNode [id=" + id + ", name=" + name + "]";
}
}
3.获取链表节点个数与查找链表中倒数第K个节点:
package com.mrz.test;
public class SingleLinkedList {
public static void main(String[] args) {
SingleLinkedListDemo singleLinkedListDemo = new SingleLinkedListDemo();
//创建五个节点
HeroNode node1 = new HeroNode(1, "刘备");
HeroNode node2 = new HeroNode(2, "关羽");
HeroNode node3 = new HeroNode(3, "张飞");
HeroNode node4 = new HeroNode(4, "吕布");
HeroNode node5 = new HeroNode(5, "貂蝉");
//添加节点
singleLinkedListDemo.addReorder(node3);
singleLinkedListDemo.addReorder(node1);
singleLinkedListDemo.addReorder(node5);
singleLinkedListDemo.addReorder(node4);
singleLinkedListDemo.addReorder(node2);
//显示节点
singleLinkedListDemo.showLinkedList(singleLinkedListDemo.getHead());
System.out.println("节点的有效个数为" + getLinkedLength(singleLinkedListDemo.getHead()));
System.out.println("倒数第三个节点为:" + findIndexReciprocal(singleLinkedListDemo.getHead(), 3));
}
//查找链表节点的个数,涉及到遍历,所以我们要传一个头节点进来
public static int getLinkedLength(HeroNode head) {
//判断链表是否为空,头结点不算在内
if(head.next == null)
return 0;
HeroNode temp = head.next;
int length = 0;
//为什么这里是temp!=null,而不是temp.next!=null
//因为我已经在上面temp = head.next,此时temp指向的是后面的节点了
while(temp != null) {
length ++;
temp = temp.next;
}
return length;
}
//查找单链表中倒数第K个节点
public static HeroNode findIndexReciprocal(HeroNode head,int index) {
//判断链表是否为空
if(head.next == null) {
return null;
}
int length = getLinkedLength(head);
//判断index是否合法
if (index > length || index <= 0) {
return null;
}
//临时辅助
HeroNode temp = head.next;
for(int i = 0; i < length - index; i++) {
temp = temp.next;
}
//返回节点
return temp;
}
}
class SingleLinkedListDemo{
private HeroNode head = new HeroNode(0, "");
//头结点是为了方便查找,此方法用来获取头结点
public HeroNode getHead() {
return head;
}
//添加节点
public void addHeroNode(HeroNode heroNode) {
//因为head节点是不能动的,因此我们需要一个辅助节点temp
HeroNode temp = head;
//添加数据是从链表的最后添加;
//如何确认最后的位置,只能 节点.next 这样遍历下去,直到值为null则说明找到了最后的位置
while(true) {
if(temp.next == null) {
break;
}
temp = temp.next;
}
temp.next = heroNode; //将新节点添加进去即可
}
//显示链表
public void showLinkedList(HeroNode head) {
//判断链表是否为空
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;
}
}
//编号排序插入
public void addReorder(HeroNode heroNode) {
HeroNode temp = head;
boolean flag = false;
while(true) {
//链表到最后
if(temp.next == null) {
flag = true;
break;
}else if (temp.next.id > heroNode.id) {
flag = true;
break;
}else if (temp.next.id == heroNode.id) {
break;
}
temp = temp.next;
}
if(flag) {
//新的节点指向temp指向下一个节点
heroNode.next = temp.next;
//temp节点指向新的节点
temp.next = heroNode;
}else {
//id重复
System.out.println("id" + heroNode.id + "重复");
}
}
}
//相当于一个节点
class HeroNode{
public int id;
public String name;
public HeroNode next; //用来指向下一个节点
public HeroNode(int id, String name) {
super();
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "HeroNode [id=" + id + ", name=" + name + "]";
}
}
4.单链表的反转(两种方法,区别在代码注释已经写好了,详细见注释):
package com.mrz.test;
import java.util.Stack;
public class SingleLinkedList {
public static void main(String[] args) {
SingleLinkedListDemo singleLinkedListDemo = new SingleLinkedListDemo();
//创建五个节点
HeroNode node1 = new HeroNode(1, "刘备");
HeroNode node2 = new HeroNode(2, "关羽");
HeroNode node3 = new HeroNode(3, "张飞");
HeroNode node4 = new HeroNode(4, "吕布");
HeroNode node5 = new HeroNode(5, "貂蝉");
//添加节点
singleLinkedListDemo.addReorder(node3);
singleLinkedListDemo.addReorder(node1);
singleLinkedListDemo.addReorder(node5);
singleLinkedListDemo.addReorder(node4);
singleLinkedListDemo.addReorder(node2);
//显示节点
//反转链表
LinkedReverse(singleLinkedListDemo.getHead());
singleLinkedListDemo.showLinkedList(singleLinkedListDemo.getHead());
System.out.println("-----------------------");
//利用栈反转链表
LinkedReverseDoStack(singleLinkedListDemo.getHead());
}
//将单链表反转
public static void LinkedReverse(HeroNode head) {
//判断链表是否为空,链表有效节点为1则不用反转
if(head.next == null || head.next.next == null) {
return;
}
//定义辅助的指针变量
HeroNode temp = head.next;
HeroNode next = null;
//新建一个指向当前节点的reverseHed
HeroNode reverseHead = new HeroNode(0, "");
//遍历原来的节点,每次遍历都取出来放入reverseHead的最前面
while(temp != null) {
//暂时保存当前节点的下一个节点
next = temp.next;
//将数据指向reverseHead这个链表的前端 第一次temp.next是null
temp.next = reverseHead.next;
//指向当前的节点,不然得到会是null
reverseHead.next = temp;
temp = next;
}
//reverseHead这个链表里就是反转后的链表,所以head指向的reverseHead就是得到一个反转的链表
head.next = reverseHead.next;
}
//利用栈来解决逆序(与上面方法区别是,栈方法只是反向打印,并没有生成反向的链表)
public static void LinkedReverseDoStack(HeroNode head) {
if(head.next == null) {
return;
}
Stack<HeroNode> stack = new Stack<HeroNode>();
HeroNode temp = head.next;
while(temp != null) {
stack.push(temp);
temp = temp.next;
}
while(stack.size() > 0) {
System.out.println(stack.pop());
}
}
}
class SingleLinkedListDemo{
private HeroNode head = new HeroNode(0, "");
//头结点是为了方便查找,此方法用来获取头结点
public HeroNode getHead() {
return head;
}
//添加节点
public void addHeroNode(HeroNode heroNode) {
//因为head节点是不能动的,因此我们需要一个辅助节点temp
HeroNode temp = head;
//添加数据是从链表的最后添加;
//如何确认最后的位置,只能 节点.next 这样遍历下去,直到值为null则说明找到了最后的位置
while(true) {
if(temp.next == null) {
break;
}
temp = temp.next;
}
temp.next = heroNode; //将新节点添加进去即可
}
//显示链表
public void showLinkedList(HeroNode head) {
//判断链表是否为空
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;
}
}
//编号排序插入
public void addReorder(HeroNode heroNode) {
HeroNode temp = head;
boolean flag = false;
while(true) {
//链表到最后
if(temp.next == null) {
flag = true;
break;
}else if (temp.next.id > heroNode.id) {
flag = true;
break;
}else if (temp.next.id == heroNode.id) {
break;
}
temp = temp.next;
}
if(flag) {
//新的节点指向temp指向下一个节点
heroNode.next = temp.next;
//temp节点指向新的节点
temp.next = heroNode;
}else {
//id重复
System.out.println("id" + heroNode.id + "重复");
}
}
}
//相当于一个节点
class HeroNode{
public int id;
public String name;
public HeroNode next; //用来指向下一个节点
public HeroNode(int id, String name) {
super();
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "HeroNode [id=" + id + ", name=" + name + "]";
}
}
5.合并两个链表(新建一个辅助链表,然后根据第一个链表和第二个链表id插入即可):
package com.mrz.test;
import java.util.Stack;
public class SingleLinkedList {
public static void main(String[] args) {
SingleLinkedListDemo singleLinkedListDemo = new SingleLinkedListDemo();
//创建五个节点
HeroNode node1 = new HeroNode(1, "刘备");
HeroNode node2 = new HeroNode(2, "关羽");
HeroNode node3 = new HeroNode(3, "张飞");
HeroNode node4 = new HeroNode(4, "吕布");
HeroNode node5 = new HeroNode(5, "貂蝉");
//添加节点
singleLinkedListDemo.addReorder(node3);
singleLinkedListDemo.addReorder(node1);
singleLinkedListDemo.addReorder(node5);
singleLinkedListDemo.addReorder(node4);
singleLinkedListDemo.addReorder(node2);
//新建一个节点
SingleLinkedListDemo singleLinkedListDemo2 = new SingleLinkedListDemo();
HeroNode node6 = new HeroNode(6, "马超");
HeroNode node7 = new HeroNode(7, "赵云");
singleLinkedListDemo.addReorder(node6);
singleLinkedListDemo.addReorder(node7);
System.out.println("合并后的链表");
mergeLinked(singleLinkedListDemo.getHead(), singleLinkedListDemo2.getHead());
}
//两个链表合并
public static void mergeLinked(HeroNode head1,HeroNode head2) {
//辅助指针变量
HeroNode temp1 = head1.next;
HeroNode temp2 = head2.next;
//创建一个中间临时链用来合并
HeroNode head3 = new HeroNode(0, "");
HeroNode temp3 = head3;
//合并三种情况
//temp1此时还有数据 temp2没有数据 就把temp1的数据加入临时链表
//temp2此时还有数据 temp1没有数据 就把temp2的数据加入临时链表
//都还有数据
while(temp1 != null || temp2 != null) {
if(temp1 != null && temp2 == null) {
temp3.next = temp1;
temp1 = temp1.next;
}else if (temp2 != null && temp1 == null) {
temp3.next = temp2;
temp2 = temp2.next;
}else {
if(temp1.id < temp2.id) {
temp3.next = temp1;
temp1 = temp1.next;
}else {
temp3.next = temp2;
temp2 = temp2.next;
}
}
//后移
temp3 = temp3.next;
}
SingleLinkedListDemo singleLinkedListDemo = new SingleLinkedListDemo();
singleLinkedListDemo.showLinkedList(head3);
}
}
class SingleLinkedListDemo{
private HeroNode head = new HeroNode(0, "");
//头结点是为了方便查找,此方法用来获取头结点
public HeroNode getHead() {
return head;
}
//添加节点
public void addHeroNode(HeroNode heroNode) {
//因为head节点是不能动的,因此我们需要一个辅助节点temp
HeroNode temp = head;
//添加数据是从链表的最后添加;
//如何确认最后的位置,只能 节点.next 这样遍历下去,直到值为null则说明找到了最后的位置
while(true) {
if(temp.next == null) {
break;
}
temp = temp.next;
}
temp.next = heroNode; //将新节点添加进去即可
}
//显示链表
public void showLinkedList(HeroNode head) {
//判断链表是否为空
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;
}
}
//编号排序插入
public void addReorder(HeroNode heroNode) {
HeroNode temp = head;
boolean flag = false;
while(true) {
//链表到最后
if(temp.next == null) {
flag = true;
break;
}else if (temp.next.id > heroNode.id) {
flag = true;
break;
}else if (temp.next.id == heroNode.id) {
break;
}
temp = temp.next;
}
if(flag) {
//新的节点指向temp指向下一个节点
heroNode.next = temp.next;
//temp节点指向新的节点
temp.next = heroNode;
}else {
//id重复
System.out.println("id" + heroNode.id + "重复");
}
}
}
//相当于一个节点
class HeroNode{
public int id;
public String name;
public HeroNode next; //用来指向下一个节点
public HeroNode(int id, String name) {
super();
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "HeroNode [id=" + id + ", name=" + name + "]";
}
}
6.双向链表的基本操作(增,删,改,查):
package com.mrz.test;
/*
* 2022-04-27
* mrz
* 双向链表的基本操作
*/
public class DoubleLinkedList {
public static void main(String[] args) {
DoubleLinkedListDemo doubleLinkedListDemo = new DoubleLinkedListDemo();
//创建五个节点
HeroNode2 node1 = new HeroNode2(1, "刘备");
HeroNode2 node2 = new HeroNode2(2, "关羽");
HeroNode2 node3 = new HeroNode2(3, "张飞");
HeroNode2 node4 = new HeroNode2(4, "吕布");
HeroNode2 node5 = new HeroNode2(5, "貂蝉");
//添加节点
doubleLinkedListDemo.addHeroNode2(node1);
doubleLinkedListDemo.addHeroNode2(node2);
doubleLinkedListDemo.addHeroNode2(node3);
doubleLinkedListDemo.addHeroNode2(node4);
doubleLinkedListDemo.addHeroNode2(node5);
doubleLinkedListDemo.showLinkedList(doubleLinkedListDemo.getHead());
doubleLinkedListDemo.updataLinkedList(new HeroNode2(5, "夏侯惇"));
System.out.println("修改后的链表");
doubleLinkedListDemo.showLinkedList(doubleLinkedListDemo.getHead());
//删除第五个
doubleLinkedListDemo.delLinkedList(5);
System.out.println("显示删除后的链表");
doubleLinkedListDemo.showLinkedList(doubleLinkedListDemo.getHead());
}
}
class DoubleLinkedListDemo{
private HeroNode2 head = new HeroNode2(0, "");
//头结点是为了方便查找,此方法用来获取头结点
public HeroNode2 getHead() {
return head;
}
//添加节点
public void addHeroNode2(HeroNode2 heroNode) {
//因为head节点是不能动的,因此我们需要一个辅助节点temp
HeroNode2 temp = head;
//添加数据是从链表的最后添加;
//如何确认最后的位置,只能 节点.next 这样遍历下去,直到值为null则说明找到了最后的位置
while(true) {
if(temp.next == null) {
break;
}
temp = temp.next;
}
temp.next = heroNode; //将新节点添加进去即可
heroNode.prev = temp;
}
//显示链表
public void showLinkedList(HeroNode2 head) {
//判断链表是否为空
if(head.next == null) {
System.out.println("链表为空");
return;
}
//因为头结点是不能动的,所以需要一个辅助变量来遍历
HeroNode2 temp = head.next;
while(true) {
if(temp == null)
break;
//System.out.println(temp.prev); //打印前驱
System.out.println(temp);
//System.out.println(temp.next); //打印后继
//System.out.println("----------------------");
temp = temp.next;
}
}
//1.刘备 2.关羽 3.张飞
//if(temp.id = hearoNode.id) temp.name = heroNode.name
//修改链表节点数据
public void updataLinkedList(HeroNode2 heroNode) {
//判断链表是否为空
if(head.next == null) {
System.out.println("链表为空");
return;
}
//定义一个辅助节点
HeroNode2 temp = head.next;
boolean flag = false;
//是否找到对应节点
while(true) {
//如果找到
if(temp.id == heroNode.id) {
flag = true;
break;
}
//防止空指针报错
if(temp.next == null) {
break;
}
temp = temp.next; //temp后移
}
if(flag) {
temp.name = heroNode.name; //将你传入的节点值赋值到原来的节点里面,实现修改
}else {
System.out.println("没有找到id为" + heroNode.id);
}
}
//删除节点
public void delLinkedList(int id) {
HeroNode2 temp = head.next;
boolean flag = false;
//找到待删除节点的前一个节点
while(true) {
//找到
if(temp.id == id) {
flag = true;
break;
}
//已经遍历到最后,需要跳出循环
if(temp.next == null) {
break;
}
temp = temp.next;
}
if(flag) {
//next是下一个,而next.next就是下下个,实现跳过中间的(删除)
//temp.next = temp.next.next;
temp.prev.next = temp.next;
//避免空指针
if(temp.next != null) {
temp.next.prev = temp.prev;
}
}else {
System.out.println("没有找到id为" + id);
}
}
//编号排序插入
public void addReorder(HeroNode2 heroNode) {
HeroNode2 temp = head;
//创建临时辅助指针
HeroNode2 current = new HeroNode2(0, "");
boolean flag = false;
while(true) {
//链表到最后
if(temp.next == null) {
flag = true;
break;
}else if (temp.next.id > heroNode.id) {
flag = true;
break;
}else if (temp.next.id == heroNode.id) {
break;
}
temp = temp.next;
}
if(flag) {
if(temp.next != null) {
heroNode.next = temp.next;
temp.next = heroNode;
heroNode.prev = temp;
heroNode.next.prev = heroNode;
//利用中间辅助指针(第二种方法)
/*current.next = temp.next;
temp.next = heroNode;
heroNode.prev = temp;
heroNode.next = current.next;
current.next.prev = heroNode;*/
}
}else {
//id重复
System.out.println("id" + heroNode.id + "重复");
}
}
}
//相当于一个节点
class HeroNode2{
public int id;
public String name;
public HeroNode2 next; //用来指向下一个节点
public HeroNode2 prev; //用来指向第一个节点
public HeroNode2(int id, String name) {
super();
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "HeroNode [id=" + id + ", name=" + name + "]";
}
}
7.环状链表解决约瑟夫问题:
有i个小孩,从第k个小孩开始报数,每报数m次,那个小孩出列,然后从出列小孩的下一个继续报数,直到所有人出列,我们需要知道的是出列的顺序(例如:有五个小孩,从第一个小孩开始报数,每报数三次,那么第一个小孩报,第二个小孩报2,第三个小孩报3,第三个小孩出列;此时链表剩余小孩为:1,2,4,5;然后从第四个小孩继续报数,再次报三个,那么第四个小孩报1,第五个小孩报2,再到第一个小孩报3,那么第一个小孩出列,一直循环到结束。)
package com.mrz.test;
/*
* 2022-04-28
* mrz
* 环状链表解决约瑟夫问题
*/
public class JosephLinkedList {
public static void main(String[] args) {
CicrleSingList list = new CicrleSingList(); //创建环形链表
list.addBoy(5);
list.countBoy(1,3,5); //从第几个小孩开始报数,每次报数几下,圈中原本有多少个小孩
}
}
class CicrleSingList{
Boy first = null;
public void addBoy(int num) {
Boy cur = null;
for(int i = 1; i <= num; i++) {
Boy boy = new Boy(i);
if(i == 1) {
first = boy;
boy.setNext(first);
cur = boy;
}else {
cur.setNext(boy);
boy.setNext(first);
cur = boy;
}
}
}
public void showBoy() {
Boy cur = first;
while(true) {
if(cur.getNext() == first) {
System.out.print("done");
break;
}
System.out.println(cur);
cur = cur.getNext();
}
}
/**
*
* @param start 从第几个小孩开始报数
* @param count 每次报数喊几下
* @param nums 圈中原本有多少小孩
*
*/
public void countBoy(int start,int count,int nums) {
if(first == null || count < 1 || count > nums) {
System.out.println("wrong");
return;
}
Boy helper = first; //帮助我们进行小孩出列操作
while(true) { //将helper指针移到了链表的最后
if(helper.getNext() == first) {
break;
}
helper = helper.getNext();
}
//如何完成从第几(start)个小孩开始报数
for(int i = 0;i < start - 1;i++) {
first = first.getNext();
helper = helper.getNext();
}
//报数,然后移出队列
while(true) {
if(helper == first) { //说明圈中只剩下一个小孩
System.out.printf("最后小孩编号=%d\n",first.getNo());
break;
}
for(int i = 0;i < count - 1;i++) { //小孩开始报数 假设需要喊三次
//后移两次,因为每个小孩本身需要报数一次,所以是用count-1
first = first.getNext();
helper = helper.getNext();
}
System.out.printf("小孩出圈,编号=%d\n",first.getNo());
first = first.getNext();
helper.setNext(first);
}
System.out.println("所有小孩出圈");
}
}
class Boy{
private int no;
private Boy next;
public Boy(int no) {
super();
this.no = no;
}
public Boy() {
super();
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public Boy getNext() {
return next;
}
public void setNext(Boy next) {
this.next = next;
}
@Override
public String toString() {
return "Boy [no=" + no + "]";
}
}
如果不明白的话可以参考学习视频【十分钟算法-图解】环形链表-约瑟夫问题_哔哩哔哩_bilibili