据说著名的犹太历史学家Josephus有过以下故事, 罗马人占领乔塔帕特, 39个犹太人与Josephus和他的朋友躲在洞中,其中39个犹太人决定自杀,
,他们的自杀方式是41个人绕成一圈,第一个人报数1,报数到3的人自杀。然后新一个人重新报数为1。最终活下来的人可以自由选择自己的命运。当剩下约瑟夫斯和他朋友时,说服了对方,选择向罗马军队投降,不再自杀。
约瑟夫斯把他的存活归因于运气或天意,他不知道是哪一个。
其实,这是一个数学问题。
前置准备
- 数据结构-链表
- 本题至少需要了解链表删除节点的逻辑,和循环链表的概念
初阶部分:链表组织结构, 模拟上述的过程求解。(本解法在洛谷中只能过部分用例。考察基本的coding)😏
进阶部分:数学和递归,迭代解法。 给定洛谷题中不需要链表组织了, 但建议读者写一份链表版本的结构代码
题目:约瑟夫环
直观解法:模拟
一种直观的解法。
将[1,n]
的数据以循环单链表的形式存储。然后模拟计数过程自杀
过程。
定义一个count
变量, 当count
报到k的时候就执行链表的删除, 然后重置count
。
- 定义一个Main类, 里面定义一个内部类
Node
。
static class Node {
int val;
Node next;
public Node(int val) {
this.val = val;
}
}
构建[1,n]
的循环单链表
public static Node createList(int n) {
if(n < 1) {
return null;
}
//构建[1...n]的序列
Node head = new Node(1);
Node tail = head;
for(int i=2;i<=n;i++) {
tail.next = new Node(i);
tail = tail.next;
}
tail.next = head;//成环。
return head;
}
模拟约瑟夫环逻辑, 返回最后存在的节点
public static Node josephusKilll(Node head, int k) {
//链表为空,单节点的循环链表,输入k不合理的逻辑。
if(head==null||head.next==head || k < 1) {
return head;
}
Node last = head;//last是head的前驱节点
while(last.next!=head) {
last = last.next;
}
int count = 0;//计数变量
while(head != last) {
if(++count == k) {
last.next = head.next;//删除head引用节点
count = 0;//重新计数
}
else {
last = last.next;
}
head = last.next;
}
return head;
}
compute函数:返回最后存活节点的编号
public static int compute(int n, int k) {
Node head = createList(n);//创建单向循环链表
head = josephusKilll(head, k);//返回约瑟夫环最后的节点。
return head==null?-1:head.val;
}
main函数处理输入输出逻辑
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StreamTokenizer in = new StreamTokenizer(br);
PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
in.nextToken();
int n = (int) in.nval;</