一、单向环形链表应用场景
提起单向环形链表,就不得不说约瑟夫问题,约瑟夫环。什么事约瑟夫问题呢?
1、约瑟夫问题(有时也称为约瑟夫斯置换,是一个计算机科学和数学中的问题。在计算机编程的算法中,类似问题又称为约瑟夫环。又称“丢手绢问题”.)
2、约瑟夫问题起源
据说,著名犹太历史学家Josephus,有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人,与Josephus,及他的朋友,躲到一个洞中,39个犹太人,决定宁愿死,也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人,该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友,并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方,才能避免被处决。Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏
3、约瑟夫问题图例
二、单向环形链表介绍
1、入队过程:
2、出队过程:
三、单向环形链表代码实现
1、代码实现思路
1、添加节点,构成环装链表
public void addKid(int nums){
//nums数据校验
if(nums<1){
System.out.println("数据错误");
return;
}
//辅助指针,帮助构成环
Kid curKid=null;
//使用for循环来创建环形链表
for (int i = 1; i <=nums ; i++) {
//根据编号创建小孩节点
Kid kid=new Kid(i);
//如果是第一个小孩
if(i==1){
first=kid;
first.setNext(first);
curKid=first;//让curKid指向第一个小孩
}else{
curKid.setNext(kid);//curkid指针指向下一个孩子
kid.setNext(first);//再指回first
curKid=kid;
}
}
}
2、遍历当前链表
public void showKid(){
//判断链表是否为空
if(first==null){
System.out.println("链表为空");
return;
}
//因为first不能动,需要创建一个帮助指针来帮助遍历
Kid curKid=first;
while (true){
System.out.printf("小孩的编号%d\n",curKid.getNo());
if(curKid.getNext()==first){
//说明已经遍历完毕
break;
}
curKid=curKid.getNext();//curKid向后移
}
}
3、计算节点出圈顺序
/**
*
* @param startNo 表示从第几个小孩开始数数
* @param countNum 表示数几下
* @param nums 表示最初有几个小孩在圈中
*/
public void countKid(int startNo,int countNum,int nums) {
if (first == null || startNo < 1 || startNo > nums) {
System.out.println("输入的数据错误");
return;
}
//创建一个辅助指针帮助小孩出圈
Kid helper = first;
while (true) {
if (helper.getNext() == first) {
//说明已经遍历完毕
break;
}
helper = helper.getNext();
}
//让小孩报数前先让frist和helper指针移动k-1次(比如报2次数,实际指针只移动了一下)
for (int j = 0; j < startNo - 1; j++) {
first = first.getNext();
helper = helper.getNext();
}
//当小孩报数前,让first和helper指针同时移动m-1次,然后出圈
while (true) {
if (helper == first) {
//说明只有一个节点
break;
}
//让first和helper同时移动countNum-1
for (int j = 0; j < countNum - 1; j++) {
first = first.getNext();
helper = helper.getNext();
}
//first指向的节点就是要出圈的节点
System.out.printf("出圈的小孩\n", first.getNo());
//first指向的节点出圈
first = first.getNext();
helper.setNext(first);
}
System.out.printf("最后留在圈里的节点%d\n", first.getNo());
}
2、代码实现
完整代码
package sparsearray;
/**
* @author shkstart
* @create 2021-08-08 16:22
*/
public class Josepfu {
public static void main(String[] args) {
CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
circleSingleLinkedList.addKid(5);
circleSingleLinkedList.showKid();
circleSingleLinkedList.countKid(1,2,5);
}
}
//创建一个环形的单向链表
class CircleSingleLinkedList{
//创建一个first节点,当前没有编号
private Kid first=null;
//添加小孩节点,构成环装链表
public void addKid(int nums){
//nums数据校验
if(nums<1){
System.out.println("数据错误");
return;
}
//辅助指针,帮助构成环
Kid curKid=null;
//使用for循环来创建环形链表
for (int i = 1; i <=nums ; i++) {
//根据编号创建小孩节点
Kid kid=new Kid(i);
//如果是第一个小孩
if(i==1){
first=kid;
first.setNext(first);
curKid=first;//让curKid指向第一个小孩
}else{
curKid.setNext(kid);//curkid指针指向下一个孩子
kid.setNext(first);//再指回first
curKid=kid;
}
}
}
//遍历当前链表
public void showKid(){
//判断链表是否为空
if(first==null){
System.out.println("链表为空");
return;
}
//因为first不能动,需要创建一个帮助指针来帮助遍历
Kid curKid=first;
while (true){
System.out.printf("小孩的编号%d\n",curKid.getNo());
if(curKid.getNext()==first){
//说明已经遍历完毕
break;
}
curKid=curKid.getNext();//curKid向后移
}
}
//计算小孩出圈顺序
/**
*
* @param startNo 表示从第几个小孩开始数数
* @param countNum 表示数几下
* @param nums 表示最初有几个小孩在圈中
*/
public void countKid(int startNo,int countNum,int nums) {
if (first == null || startNo < 1 || startNo > nums) {
System.out.println("输入的数据错误");
return;
}
//创建一个辅助指针帮助小孩出圈
Kid helper = first;
while (true) {
if (helper.getNext() == first) {
//说明已经遍历完毕
break;
}
helper = helper.getNext();
}
//让小孩报数前先让frist和helper指针移动k-1次(比如报2次数,实际指针只移动了一下)
for (int j = 0; j < startNo - 1; j++) {
first = first.getNext();
helper = helper.getNext();
}
//当小孩报数前,让first和helper指针同时移动m-1次,然后出圈
while (true) {
if (helper == first) {
//说明只有一个节点
break;
}
//让first和helper同时移动countNum-1
for (int j = 0; j < countNum - 1; j++) {
first = first.getNext();
helper = helper.getNext();
}
//first指向的节点就是要出圈的节点
System.out.printf("出圈的小孩\n", first.getNo());
//first指向的节点出圈
first = first.getNext();
helper.setNext(first);
}
System.out.printf("最后留在圈里的节点%d\n", first.getNo());
}
}
//创建Kid类,表示节点
class Kid{
private int no;//小孩编号
private Kid next;//表示下一个节点
public Kid(int no) {
this.no = no;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public Kid getNext() {
return next;
}
public void setNext(Kid next) {
this.next = next;
}
}