最近做了一道笔试题目,题目大意如下:
假设N个人围成一圈,从1开始循环报数,当报到M时该人被枪毙,然后剩下的人继续从1开始报数,直到剩下最后一个幸存者为止。例如,假设有编号1-6个人报数,M=5,则被枪毙的人依次是:5,4,6,2,3幸存者为1。
然后现在假设有2K个人围成一圈,其中前K个为好人,编号为1-K,后面K个为坏人,求使得最终幸存者为好人的最小的M值。
题目给出时限大约为25分钟。在这么短的时间内用某种语言写一个完整的算法代码出来比较有难度,个人理解是,这道题看的应该是你所用到的数据结构和思路过程,而不应该纠结于使用什么语言,以及如何去定义这个数据结构,这里给出Java语言编写的代码如下:
//定义结点类
class Node{
int data; //结点的值
Node next; //指向下一个结点的指针
Node(int data){
this.data=data;
}
}
//定义单循环链表
class Link{
Node head; //链表头结点
int size;
//链表长度
//创建一个长度为len的链表 链表的结点数据为1,2,3、、
Node createLink(int len){
if(len>=1){
head=new Node(1);
Node p=head;
head.next=null;
for(int i=2;i<=len;i++){ //循环定义结点的值
Node node=new Node(i);
p.next=node;
node=p;
}
this.size=len;
p.next=head; //把链表变成循环链表
return head; //返回头指针
}else{
return null; //若传入的长度参数值小于1 则返回空值
}
}
//删除一个链表结点
void removeNode(Node p){
//此处忽略
}
}
//处理需求的类 获取使得最终幸存者为好人的最小的M值
public class GetLeastM {
//定义算法获取最终幸存者编号 参数N为初始人数 M指报数的第M人被枪毙
public int getLastOne(int N,int M){
Link link=new Link();
Node p=link.createLink(N); //获取新创建链表的头结点
int temp=1;
while(link.size>1){ //循环知道链表长度为1 也即只剩下最终幸存者时结束
p=p.next;
temp++;
if(temp==M){
temp=1; //重置temp的值为1 也就是重新报数
Node q=p;
link.removeNode(q); //被枪毙的那个人被剔除
}
}
return p.data;
//返回最终幸存者的编号
}
//获取使得最终幸存者为好人的最小的M(也即幸存者编号小于特定值K的最小M)
//参数K为好人与坏人的个数 其中好人为前K个,坏人为后K个 总计2K个人围成一圈
public int getLeastM(int K){
int M=0;
for(int i=2;;i++){ //M至少比1大
M=getLastOne(K, i); //获取幸存者编号
if(M<K){
return M;
}
}
}
}
做完这道题的一个教训是,在有限的时间内要想比较完善的给出出题者满意的答案,首先应该给出最能体现你思路的关键算法,并定义你所使用的数据结构,至于该数据结构里具体有哪些属性和方法,则不应该是你要纠结的东西,比如该题如何创建链表和删除链表结点的方法,给出个方法定义以及返回值即可,没有必要给出完整的实现过程,因为这不是该题的重点。而我因为当时纠结于这些东西导致超时没有完成,颇为遗憾,毕竟这道题本身并不难。