原文地址:http://blog.fishc.com/1959.html
约瑟夫问题
据说著名犹太历史学家 Josephus有过以下的故事:
在罗马人占领乔塔帕特后,39个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。
理论付诸实践
小甲鱼:理论为什么能够付诸实践?
热心鱼油:你TMD在说啥*&(*%@……!
小甲鱼:我的意思是约瑟夫问题跟我们讲的循环链表有啥关系?
某女鱼油:它们都带套!
小甲鱼:真聪明,亲一个^_^
小甲鱼:对的,约瑟夫问题里边41个人是围成一个圆圈,我们的循环链表也是一个圆圈,所以可以模拟并让计算机运行告诉我们结果!
问题:用循环链表模拟约瑟夫问题,把41个人自杀的顺序编号输出。
//n个人围圈报数,报m出列,最后剩下的是几号?
#include <stdio.h>
#include <stdlib.h>
typedef struct node
{
int data;
struct node *next;
}node;
node *create(int n)
{
node *p = NULL, *head;
head = (node*)malloc(sizeof (node ));
p = head;
node *s;
int i = 1;
if( 0 != n )
{
while( i <= n )
{
s = (node *)malloc(sizeof (node));
s->data = i++; // 为循环链表初始化,第一个结点为1,第二个结点为2。
p->next = s;
p = s;
}
s->next = head->next;
}
free(head);
return s->next ;
}
int main()
{
int n = 41;
int m = 3;
int i;
node *p = create(n);
node *temp;
m %= n; // m在这里是等于2
while (p != p->next )
{
for (i = 1; i < m-1; i++)
{
p = p->next ;
}
printf("%d->", p->next->data );
temp = p->next ; //删除第m个节点
p->next = temp->next ;
free(temp);
p = p->next ;
}
printf("%d\n", p->data );
return 0;
}
另一种实现:
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
typedef struct Node
{
int m_num;
Node *next;
}node;
void initList(int num,node **pHead)
{
if((*pHead)==NULL)
(*pHead)=(node*)malloc(sizeof(node));
if(!(*pHead))
exit(0);
(*pHead)->m_num=1;
(*pHead)->next=(*pHead);
node *cur=(*pHead);
for(int i=2;i<=num;i++)
{
node *tmp=(node*)malloc(sizeof(node));
if(!tmp)
exit(0);
tmp->next=*pHead;
tmp->m_num=i;
cur->next=tmp;
cur=tmp;
}
}
void printNum(node* pHead)
{
node *p1=pHead;
node *p2=NULL;
node *p3=NULL;
while(1)
{
p2=p1->next;
p3=p2->next;
if(p1==p3)
break;
printf("%d,",p3->m_num);
p2->next=p3->next;
p1=p3->next;
free(p3);
}
pHead=p1;
printf("\n活下来的人:%d,%d\n",pHead->m_num,pHead->next->m_num);
}
int _tmain(int argc, _TCHAR* argv[])
{
node *pHead=NULL;
initList(41,&pHead);
printNum(pHead);
system("pause");
return 0;
}
提高挑战难度:
编号为1~N的N个人按顺时针方向围坐一圈,每人持有一个密码(正整数,可以自由输入),开始人选一个正整数作为报数上限值M,从第一个人按顺时针方向自1开始顺序报数,报道M时停止报数。报M的人出列,将他的密码作为新的M值,从他顺时针方向上的下一个人开始从1报数,如此下去,直至所有人全部出列为止。
#include <stdio.h>
#include <stdlib.h>
#define MAX_NODE_NUM 100
#define TRUE 1U
#define FALSE 0U
typedef struct NodeType
{
int id;
int cipher;
struct NodeType *next;
} NodeType;
/* 创建单向循环链表 */
static void CreaList(NodeType **, const int);
/* 运行"约瑟夫环"问题 */
static void StatGame(NodeType **, int);
/* 打印循环链表 */
static void PrntList(const NodeType *);
/* 得到一个结点 */
static NodeType *GetNode(const int, const int);
/* 测试链表是否为空, 空为TRUE,非空为FALSE */
static unsigned EmptyList(const NodeType *);
int main(void)
{
int n, m;
NodeType *pHead = NULL;
while (1)
{
printf("请输入人数n(最多%d个): ", MAX_NODE_NUM);
scanf("%d", &n);
printf("和初始密码m: ");
scanf("%d", &m);
if (n > MAX_NODE_NUM)
{
printf("人数太多,请重新输入!\n");
continue;
}
else
break;
}
CreaList(&pHead, n);
printf("\n------------ 循环链表原始打印 -------------\n");
PrntList(pHead);
printf("\n-------------删除出队情况打印 -------------\n");
StatGame(&pHead, m);
}
static void CreaList(NodeType **ppHead, const int n)
{
int i, iCipher;
NodeType *pNew, *pCur;
for (i = 1; i <= n; i++)
{
printf("输入第%d个人的密码: ", i);
scanf("%d", &iCipher);
pNew = GetNode(i, iCipher);
if (*ppHead == NULL)
{
*ppHead = pCur = pNew;
pCur->next = *ppHead;
}
else
{
pNew->next = pCur->next;
pCur->next = pNew;
pCur = pNew;
}
}
printf("完成单向循环链表的创建!\n");
}
static void StatGame(NodeType **ppHead, int iCipher)
{
int iCounter, iFlag = 1;
NodeType *pPrv, *pCur, *pDel;
pPrv = pCur = *ppHead;
/* 将pPrv初始为指向尾结点,为删除作好准备 */
while (pPrv->next != *ppHead)
pPrv = pPrv->next;
while (iFlag)
{
for (iCounter = 1; iCounter < iCipher; iCounter++)
{
pPrv = pCur;
pCur = pCur->next;
}
if (pPrv == pCur)
iFlag = 0;
pDel = pCur; /* 删除pCur指向的结点,即有人出列 */
pPrv->next = pCur->next;
pCur = pCur->next;
iCipher = pDel->cipher;
printf("第%d个人出列, 密码: %d\n", pDel->id, pDel->cipher);
free(pDel);
}
*ppHead = NULL;
getchar();
}
static void PrntList(const NodeType *pHead)
{
const NodeType *pCur = pHead;
if (EmptyList(pHead))
return;
do
{
printf("第%d个人, 密码: %d\n", pCur->id, pCur->cipher);
pCur = pCur->next;
}
while (pCur != pHead);
getchar();
}
static NodeType *GetNode(const int iId, const int iCipher)
{
NodeType *pNew;
pNew = (NodeType *)malloc(sizeof(NodeType));
if(!pNew)
{
printf("Error, the memory is not enough!\n");
exit(-1);
}
pNew->id = iId;
pNew->cipher = iCipher;
pNew->next = NULL;
return pNew;
}
static unsigned EmptyList(const NodeType *pHead)
{
if(!pHead)
{
printf("The list is empty!\n");
return TRUE;
}
return FALSE;
}