用单循环链表来耍约瑟夫环游戏
701
2013-2-6

一群小孩围成一圈,每个小孩都会带有一个随机的密码。然后设定一个数m,从第一个小孩数起,数到第m个的时候,该小孩离开。小孩离开时,其携带的密码将更新这个m值,顺序往下数的第m个小孩会继续出列。依次这样数下去,最后一个小孩是胜利者,问:胜利者是第几个小孩?

这就是大家所熟知的约瑟夫环。

单循环链表天然地很适合解决这个问题,下面用C语言实现了一下:

#include
#include
#include
#define OK 1;
#define ERROR 0;
#define OVERFLOW -2;

typedef int ElemType;
typedef struct Node
{
    ElemType num;
    ElemType password;
    struct Node *next;
}Node;
typedef struct Node CLinkList; /* 定义LinkList */

void main(){
	int num=10;
	int m=10;
	CLinkList *L;
	/* 输入约瑟夫环的总结点数*/
	printf("有多少个小孩参与游戏(设定小于100):");
	scanf("%d",&num);
	if(num=100){
	    /* 如果输入的数字超出范围,就默认总结点数为10 */
	    printf("你的输入不符合,下面采用默认数字10\n");
	    num=10;
	}
	/* 输入第一个需要出列的数字 */
	printf("第一个报到哪个数必须出列 (设定小于100):");
	scanf("%d",&m);
	if(m=100){
	    printf("你的输入不符合,下面采用默认数字10\n");
	    m=10;
	}
    InitList(L);
    Creat_Joseph(L,num);
    ListTraverse(L,num);
	Joseph_Run(L,m);
}

/* 释放删除的结点 */
void FreeNode(CLinkList *p){
	CLinkList *temp=p;
	while(temp->next!=p)
		temp=temp->next;
	temp->next=p->next;
	printf("编号%3d的小孩离队了,他携带的密码是%3d\n",p->num, p->password);
	free(p);
}

/* 初始化结点 */
int InitList(CLinkList *l){
	l=(CLinkList *) malloc (sizeof(CLinkList));
	if(!l)
		return OVERFLOW;
	return OK;
}
/* 创建每个结点携带的密码 */
void Creat_Joseph(CLinkList *l,int length){
	int i;
	CLinkList *p=l;
	l->next=l;
	l->num=1;
	srand( (unsigned)time(NULL));
	l->password=rand()%(length+3)+1;
	printf("以下小孩(约瑟夫环结点)携带的密码随机生成的:\n");
	/* 打印约瑟夫环的第一个结点 */
	printf("小孩编号: %3d      带的密码: %3d\n",l->num,l->password);
	for(i=1;inext=temp;
		temp->next=l;
		temp->num=i+1;
		temp->password=rand()%(2*length)+1;
		/* 打印除第一个结点外的其它结点 */
		printf("小孩编号: %3d      带的密码: %3d\n",temp->num,temp->password);
		p=p->next;
	}
}

void ListTraverse(CLinkList *L,int length)
{
    int i;
    CLinkList *p=L->next;
    for(i = 0; i < length; i++)     {         printf("%d:%d ",p->num, p->password);
        p=p->next;
    }
    printf("\n");
    return OK;
}

/* 根据结点携带的密码释放下一个结点 */
void Joseph_Run(CLinkList *l, int m){
    int i,k;
    CLinkList *temp,*p;
	if(l->next==l)
        /* 打印最后一个结点 */
		printf("最后编号 %d 的小孩获胜!",l->num);
	else{
		temp = l;
		for(i=1; inum);
		    temp=temp->next;
		}
		p=temp->next;
		k=temp->password;
		/* 释放中招的结点 */
		FreeNode(temp);
		/* 递归 */
		Joseph_Run(p,k);
	}
}

程序运行结果:

有多少个小孩参与游戏(设定小于100):6
第一个报到哪个数必须出列 (设定小于100):8
以下小孩(约瑟夫环结点)携带的密码随机生成的:
小孩编号:   1      带的密码:   5
小孩编号:   2      带的密码:   9
小孩编号:   3      带的密码:  12
小孩编号:   4      带的密码:   2
小孩编号:   5      带的密码:   5
小孩编号:   6      带的密码:   2
2:9 3:12 4:2 5:5 6:2 1:5
编号  1的小孩安全
编号  2的小孩安全
编号  3的小孩安全
编号  4的小孩安全
编号  5的小孩安全
编号  6的小孩安全
编号  1的小孩安全
编号  2的小孩离队了,他携带的密码是  9
编号  3的小孩安全
编号  4的小孩安全
编号  5的小孩安全
编号  6的小孩安全
编号  1的小孩安全
编号  3的小孩安全
编号  4的小孩安全
编号  5的小孩安全
编号  6的小孩离队了,他携带的密码是  2
编号  1的小孩安全
编号  3的小孩离队了,他携带的密码是 12
编号  4的小孩安全
编号  5的小孩安全
编号  1的小孩安全
编号  4的小孩安全
编号  5的小孩安全
编号  1的小孩安全
编号  4的小孩安全
编号  5的小孩安全
编号  1的小孩安全
编号  4的小孩安全
编号  5的小孩安全
编号  1的小孩离队了,他携带的密码是  5
编号  4的小孩安全
编号  5的小孩安全
编号  4的小孩安全
编号  5的小孩安全
编号  4的小孩离队了,他携带的密码是  2
最后编号 5 的小孩获胜!
Process returned 23 (0x17)   execution time : 8.506 s
Press any key to continue.

以上内容仅供参考,原文来自:http://www.nowamagic.net/librarys/veda/detail/1867