本章学习目录
- 问题的引出:17世纪的法国数学家加斯帕在《数目的游戏问题》中讲了这样一个故事:15个教徒和15 个非教徒在深海上遇险,必须将一半的人投入海中,其余的人才能幸免于难,于是想了一个办法:30个人围成一圆圈,从第一个人开始依次报数,每数到第九个人就将他扔入大海,如此循环进行直到仅余15个人为止。问怎样排法,才能使每次投入大海的都是非教徒。
- 问题的解决:循环链表可以实现上述问题的解决
- 页内目录
一,环形链表的介绍
二,约瑟夫环的问题
三,环形链表的节点类
四,环形链表的建立和打印
五,约瑟夫环问题的解决
一,环形链表的介绍
- 循环链表:是另一种形式的链式存储结构。类似于单链表,但是它的特点是表中最后一个结点的指针域指向头结点,整个链表形成一个环。
- 逻辑结构图:
二,约瑟夫环的问题
- 约瑟夫环问题:(Josephus问题)约瑟夫问题(有时也称为约瑟夫斯置换,是一个计算机科学和数学中的问题。在计算机编程的算法中,类似问题又称为约瑟夫环。又称“丢手绢问题”.),
- 理解:即解决在一个n节点的圈子中,从某一个位置开始数,数到m并取出该节点,反复操作,直至环取空;
三,环形链表的节点类
package com.环形链表;
public class Node {
public int no;//编号
public Node next;//指向下一个节点
public Node(int no) {
this.no = no;
}
}
四,环形链表的建立和打印
public class CircleSingleLinkedList {
//创建第一个节点
private Node firstNode=null;
//方法一:构建环
//方法二:打印环内节点
//方法三:约瑟夫环问题的解决(配合法一用)
}
- 构建环:
/**
* 向环中添加节点,即构建环形链表
* @param num,添加节点的数量
*/
public void addNodeNum(int num){
//判断num合法
if (num<1){
System.out.println(num+"不能构成环");
return;
}
//使用for循环来创建环形链表
//定义一个辅助节点,初始值为firstNode
Node temp=firstNode;
for (int i=1;i<=num;i++){
//根据编号创建环形链表内的节点
Node node=new Node(i);
if (i==1){
firstNode=node;
firstNode.next=firstNode;//构成环
temp=firstNode;
}else{
temp.next=node;//将辅助节点后移
node.next=firstNode;//成环,让最后生成的节点指向第一个节点
temp=node;
}
}
}
- 打印环内节点
/**
* 打印环形链表
*/
public void printNodeList(){
//判断链表是否为空
if (firstNode==null){
System.out.println("链表为空");
return;
}
//创建辅助节点,直接从firstNode开始遍历,
Node temp=firstNode;
while (true){
System.out.println("节点编号"+temp.no);
if (temp.next==firstNode){
break;
}else {
temp=temp.next;
}
}
}
- 约瑟夫环问题:
/**
* 约瑟夫环问题
* @param startnum 从startnum节点开始数
* @param countnum 数countnum次
* @param num 总共有num个节点
*/
public void findNodeBynum(int startnum,int countnum,int num){
//先校验数据合理性
if (startnum<1||startnum>num||firstNode==null){
System.out.println("参数有误");
return;
}
//创建要给辅助指针,帮助完成小孩出圈
Node temp=firstNode;
//通过while循环,将temp指向first的前一个节点
while (true){
if (temp.next==firstNode){
break;
}
temp=temp.next;
}
//for循环目的:让first和temp分别移动到第startnum的节点和startnum的前一个处
for (int i=1;i<startnum;i++){
firstNode=firstNode.next;
temp=temp.next;
}
System.out.println("开始节点:"+firstNode.no);
//用于计算第count次出圈
int count=0;
//通过while循环找出要出来的节点
while (true){
if (firstNode==temp){
break;
}
//for循环找出要出圈的节点
for (int j=1;j<countnum;j++){
firstNode=firstNode.next;
temp=temp.next;
}
count++;
System.out.println("第"+count+"次出来的节点"+firstNode.no);
//让对应节点出圈,并保证环的连通性
firstNode=firstNode.next;
temp.next=firstNode;
}
System.out.println("最后的节点是"+temp.no);
}
五,约瑟夫环问题的解决
- 环形链表的节点类在目录三
- 操作类如下:
package com.环形链表;
/**
*先构建环形链表
*对链表进行出圈
*/
public class CircleSingleLinkedList {
//创建第一个节点
private Node firstNode=null;
/**
* 向环中添加节点,即构建环形链表
* @param num,添加节点的数量
*/
public void addNodeNum(int num){
//判断num合法
if (num<1){
System.out.println(num+"不能构成环");
return;
}
//使用for循环来创建环形链表
//定义一个辅助节点,初始值为firstNode
Node temp=firstNode;
for (int i=1;i<=num;i++){
//根据编号创建环形链表内的节点
Node node=new Node(i);
if (i==1){
firstNode=node;
firstNode.next=firstNode;//构成环
temp=firstNode;
}else{
temp.next=node;//将辅助节点后移
node.next=firstNode;//成环,让最后生成的节点指向第一个节点
temp=node;
}
}
}
/**
* 约瑟夫环问题(和上面的方法一起使用)
* @param startnum 从startnum节点开始数
* @param countnum 数countnum次
* @param num 总共有num个节点
*/
public void findNodeBynum(int startnum,int countnum,int num){
//先校验数据合理性
if (startnum<1||startnum>num||firstNode==null){
System.out.println("参数有误");
return;
}
//创建要给辅助指针,帮助完成小孩出圈
Node temp=firstNode;
//通过while循环,将temp指向first的前一个节点
while (true){
if (temp.next==firstNode){
break;
}
temp=temp.next;
}
//for循环目的:让first和temp分别移动到第startnum的节点和startnum的前一个处
for (int i=1;i<startnum;i++){
firstNode=firstNode.next;
temp=temp.next;
}
System.out.println(firstNode.no);
//用于计算第count次出圈
int count=0;
//通过while循环找出要出来的节点
while (true){
if (firstNode==temp){
break;
}
//for循环找出要出圈的节点
for (int j=1;j<countnum;j++){
firstNode=firstNode.next;
temp=temp.next;
}
count++;
System.out.println("第"+count+"次出来的节点"+firstNode.no);
//让对应节点出圈,并保证环的连通性
firstNode=firstNode.next;
temp.next=firstNode;
}
System.out.println("最后的节点是"+temp.no);
}
}