数据结构
第二章环状单向链表解决约瑟夫问题
一、什么是约瑟夫环?
约瑟夫环是指N个人围成一个圈,设每个人的编号为1,2,…,N。从指定的编号为K的人从1开始报数,数到数字M的人出圈,又从他的下一位开始从1开始报数,数到数字M的人出圈,以此类推,直到所有人出圈为止,求出出圈的编号顺序。
二、解题步骤
1.逻辑分析
假设从1开始,即K=1
N=5,共5个人
M=2,数到2出圈
思路:
- 将first节点移动到开始数数的节点
- 建立辅助指针,指向first的上一个节点
- 将first节点顺着链表移动m-1次(因为first节点本身也是要数数的,从1开始,数到m只需移动m-1次)
- temp节点也跟随first节点移动m-1次
- 移动之后first指向的节点就是要出圈的节点
- 打印输出
- 执行出圈操作,移动first节点到出圈节点的下一个节点,temp节点的next指向first节点。
- 出圈节点成功出圈,从first节点进行下一次遍历
出圈顺序为:
出圈CNode [id=2]
出圈CNode [id=4]
出圈CNode [id=1]
出圈CNode [id=5]
出圈CNode [id=3]
2.代码实现
public void count(int startNo,int countNum,int nums) {
if(first==null || startNo>nums) {
System.out.println("Error");
}
//先将first移动到要开始报数的节点
for(int i=1;i<startNo;i++) {
first=first.next;
}
CNode temp = first;//定义辅助节点,指向first
while(temp.next!=first)
{
/*
* 移动temp,将temp指向first节点的上一个节点
* 当first指向的节点出圈,那么temp节点可以将保证单项链表保持环形
*/
temp=temp.next;
}
while(true) {
if(temp==first) {//当temp和first指向同一个节点,那么说明环形链表只剩下一个节点
System.out.println("出圈"+temp);
break;
}
for(int i=1;i<countNum;i++) {
first=first.next;//因为first节点也需要报数,所以只需将first往下移动countNum-1次
temp=temp.next;//temp也要移动,除非链表中只剩下一个节点,不然temp始终指向first的上一个节点
}
System.out.println("出圈"+first);
first=first.next;//将first指向出圈节点的下一个节点
temp.next=first;//temp指向节点的next指向first
//此时出圈节点就脱离环形链表
}
}