约瑟夫环
约瑟夫问题有时也称为约瑟夫斯置换,是一个计算机科学和数学中的问题。在计算机编程的算法中,类似问题又称为约瑟夫环。又称“丢手绢问题”.
约瑟夫问题简单描述就是N个人围成一圈,从第一个开始报数,第M个将被杀掉,然后下一个人继续从一开始报数,直到最后剩下一个人为止。
方法一:链表解决
int JoseDelete(HNode h, int M, int k, int s){ //出局函数
// M : 开始人数 k : 报数到K,结束本轮,即报数密码 s : 要求最终剩余人数
int i;
PNode p=h,q;
while(M>s)
{
for(i=1;i<k-1;i++) // 报数
{
p=p->next;
}
q=p->next;
p->next=q->next;
printf("出局的人为:%d号\n",q->Num);
free(q);
p=p->next;
M--;
}
// 遍历函数,将最后剩余的s人编号输出
TraverseList(p,s);
return 1;
}
总程序:
#include <stdio.h>
#include <malloc.h>
/*构建结构体*/
typedef struct Node{
int Num;
struct Node *next;
}JoseNode, *PNode, *HNode;
int JoseInit(HNode h){ // 初始化循环单链表
if (!h)
{
printf("初始化链表错误!\n");
return 0;
}
(h)->next = (h);//循环单链表
return 1;
}
int JoseInsert(JoseNode *h, int pos, int x) { //单链表插入操作
PNode p=h,q;
int i=1;
if (pos == 1)/*尾插法*/
{
p->Num = x;
p->next = p;
return 1;
}
while(i<pos-1)
{
p=p->next;
i++;
}
q=(PNode)malloc(sizeof(JoseNode));
q->Num=x;
q->next=p->next;
p->next=q;
return 1;
}
void TraverseList(HNode h, int M){ /*遍历*/
int i = 0;
PNode p = h;
printf("剩下的人的编号为:\n");
while (i<M)
{
printf("%d\t", p->Num);//
p = p->next;
i++;
}
printf("\n");
}
int JoseDelete(HNode h, int M, int k, int s){ //出局函数
int i;
PNode p=h,q;
while(M>s)
{
for(i=1;i<k-1;i++)
{
p=p->next;
}
q=p->next;
p->next=q->next;
printf("出局的人为:%d号\n",q->Num);
free(q);
p=p->next;
M--;
}
TraverseList(p,s);
return 1;
}
int main(){
int i;//计数器
int N;//参与的人数
int k;//报数密码
int s; // 要求剩余人数
printf("请输入参与人数:");
scanf("%d",&N);
printf("请输入出局密码:");
scanf("%d",&k);
printf("请输入要剩余人数:");
scanf("%d",&s);
HNode h = ((HNode)malloc(sizeof(JoseNode))); //得到头结点
JoseInit(h); ///初始化单链表
for (i = 1; i <= N; i++){ //将编号插入到循环单链表中
JoseInsert(h, i, i);
}
// TraverseList(h,N); //遍历单链表
if(k > 1) { //出局函数
JoseDelete(h, N, k, s);
}else{
for(i = 1; i < N - s; i++){
printf("出局的人为:%d号\n",i);
}
printf("剩余人员:\n");
for(i; i <= N;i++){
printf("%d\t",i);
}
}
printf("\n");
return 0;
}
方法二:数组
void left_num(int* a,int n,int m) { // n人参与 m为报数密码
int out = 0,count = 0,i = 0; //out为出去的人数,count为报数,i为目前报到第几个人
int *p = a;
int num = 0;
for(num = 0;num < n;num++) {
*(a+num) = num+1;
} //为n个人编号1-n
while (out < n-1) {
if (*(p+i) != 0) {
count ++; //不等于0才报数+!
}
if (count == m) {
count = 0;
*(p+i) = 0;
out++; //报道m那么,出列人数out+1,且内容置0,报数置0
printf("%d\n",i+1);
}
i++;
if (i == n) {
i = 0; //到队尾重头开始
}
}
//输出剩下的人
for (num = 0; num < n; num++) {
if (*(a+num) != 0) {
printf("left num:%d\n",*(a+num));
}
}
}