约瑟夫问题的描述:有N个人坐成一圈,编号为1之N,从编号为1的人开始传递热马铃薯,M次传递之后,持有马铃薯的人退出游戏,然后从退出人的下一个继续游戏,传递M次之后,持有人退出继续游戏。最终留下来的人获胜。
用list实现
list<int> JosephusProblem_solution1(int m,int n){
list<int> jose;//用于编号1至N
list<int> res;//用于记录退出编号的次序;
int mp;
for(int i=1;i<=n;i++){
jose.push_back(i);//编号
}
list<int>::iterator litr=jose.begin();
//这里必须明白list.begin()和list.end()
//为什么litr只自加m-1次?
//为什么jose.end()时litr=jose.end(),而不用litr++?
for(int i=0;i<n;i++){
for(int j=0;j<m-1;j++){
if(++litr==jose.end()){
litr=jose.begin();
}
}
list<int>::iterator del = litr;
res.push_back(*litr);
//避免删除最后一个元素时,找不到下一个起始点
if(++litr==jose.end()){
litr=jose.begin();
}
jose.erase(del);
}
return res;
}
单向循环链表实现
int JosephusProblem_solution3(int m,int n){
if(n<1||m<1){
return -1;
}
struct listNode{
int num;
struct listNode *next;
listNode(int n=0,listNode *p = NULL){
num=n;next = p;
}
};
listNode *head = new listNode(1);
listNode *CurrentNode = head;
listNode *tail = NULL;
for(int i=2;i<=n;i++){
CurrentNode->next=new listNode(i);
CurrentNode = CurrentNode->next;
}
CurrentNode->next = head;
tail=CurrentNode;
free(CurrentNode);
//cout<<"--------------"<<endl;
//这里自己画图就可以理解
//head为tail的前一个节点,且同步移动
//移动M次后tail->next = head->next,即删除了第M个元素,
//再将head作为tail前一个元素即可
while(tail!=head){
for(int i=0;i<m-1;i++){
tail=head;
head=head->next;
}
tail->next = head->next;
free(head);
head=tail->next;
}
//cout<<tail->num<<endl;
return tail->num;
}
数学方法实现
int JosephusProblem_solution2(int m,int n){
if(m<1||n<1){
return -1;
}
int *res = new int[n+1];
res[1] = 0;
for(int i=2;i<=n;i++){
res[i] = (res[i-1]+m)%i;
}
int result = res[n];
delete []res;
return result+1;
}
有不严谨的地方,见谅!