魔术师发牌问题
问题描述:
魔术师利用一副牌中的13张黑牌,预先将他们排好后叠放在一起,牌面朝下。对观众说:“我不看牌,只数数就可以猜到每张牌是什么,我大声数数,你们听,不信?现场演示。”魔术师将最上面的那张牌数为1,把他翻过来正好是黑桃A,将黑桃A放在桌子上,第二次数1,2,将第一张牌放在这些牌的下面,将第二张牌翻过来,正好是黑桃2,也将它放在桌子上这样依次进行将13张牌全部翻出,准确无误。
问题:牌的开始顺序是如何安排的?
用循环链表来解决
解题思路:好像是约瑟夫问题的反向求解。先看一下图
下方是已知的最后顺序,且已知循环链表有13个元素,所以建立13个空格代表循环链表,从1开始依次往里填入数,只能数空的空格,有数的空格要跳过,因为数到的牌会被抽走,最后填满如图所示,按照这个思路写代码。
public class MoshushifapaiTest {
public static void magician(){
Node first = new Node(1);
first.nextNode = first;
Node last = first;
// 1:先创建一个空的 循环链表,确定一个位置为起始位置,data为1
for(int i = 2;i<14;i++){
Node next = new Node();
next.nextNode = first;
last.nextNode = next;
last = next;
}
// 2:从data为1node的下一个node开始数,数两个,依次填入牌的号码,
// 直到数到最大的13次,将13填入循环链表中
Node now = first;
for(int i=2;i<14;i++){
int j = 1;
// i:应该数的次数,比如填入i时,应该在链表中数i次。
// j:实际数的次数,比如填入i时,应该数i次,当时当前数到了j次,j<=i
// 当 i=j 时往循环链表中填入 当前数字
// 从填入的下一个开始数1
Node next = now.nextNode;
while(true){
// data 为0 说明是空的node,如果不是空的node 要跳过,且不记录数的次数
if(next.data==0){
// 当应该数的次数和实际数的次数相等时,往当前node填入此数,并且now为当前node
if(j==i){
next.data = i;
now = next;
// 跳出while
break;
}
j++;
}
next = next.nextNode;
}
}
System.out.println();
while(true){
now = first;
boolean flag = true;
while(true){
if(now.data!=1){
System.out.print(now);
now = now.nextNode;
}else if(now.data==1&&flag){
System.out.print(now);
flag = false;
now = now.nextNode;
}else{
break;
}
}
break;
}
}
public static void main(String[] args) {
magician();
}
}
class Node{
int data;
Node nextNode;
public Node(int data){
this.data = data;
}
public Node(){
}
@Override
public String toString() {
return "|黑桃"+data+"|";
}
}