BlackA,Talk is cheap,Show me the code.
前两天有一个同学问了我一道约瑟夫环的问题,给他做完之后发现自己也学到不少,在这里写一篇文章记录一下。
约瑟夫问题是个有名的问题:N个人围成一圈,从第一个开始报数,每报数到第 M个人该人就被拉出去杀掉, 剩下的人仍在围成一圈。然后再由下一个重新报数,直到最后剩下一个,其余人都将被杀掉。 约瑟夫环运作如下: 1. 一群人围在一起坐成环状(如: N) 2. 从某个编号开始报数(如: K) 3. 数到某个数(如:M)的时候,此人出列,下一个人重新报数 4. 一直循环,直到所有人出列,约瑟夫环结束 例如 N = 6,M = 5,被杀掉的顺序是:5,4,6,2,3,1
1、使用 CreateList 函数建立循环链表(表示围成一圈的人),队列长度由参数N指定(元素编号为1~N,表示每个人的编号),该函数返回循环队列的第一个元素的指针。
2、设置 now 指针指向第一个元素(表示当前报数的人,从第一个人开始报数)。
3、设置 i 的值为1(表示当前报道的数,默认从1开始报数)。
4、判断当前队列是否为空(如果为空表示所有人均已经出局,程序结束,如果不为空则执行以下循环)。
5、判断这个人报的数是否为 m 。
6、如果是,表示这个人出局,保存这个人的下一个人(因为这个人会被删除,无法得到下一个人),调用 DelItem 函数删除节点(该函数会返回被删除节点的序号),输出被删除节点的序号,设置报数重新开始,设置当前人为下一个人。
7、如果不是就报数 +1,继续下一个人。
// 初始化循环队列
list = CreateList(N);
// 当前指向队列头
now = list;
// 从第一个数开始报数,当队列不空
i = 1;
while(!is_empty(list))
{
// 如果这个人报到M,出局
if(i == M)
{
// 这个人的下一个人
next = now->next;
// 调用函数删除这个人人
num = DelItem(list,now);
// 输出被删除人的序号
printf("%d\n",num);
// 从1开始再次报数
i = 1;
now = next;
}
else
{
// 报数+1
i++;
//下一个人
now = now->next;
}
}
#include <stdio.h>
#include <stdlib.h>
// 节点
typedef struct node
{
int num; // 节点的序号
struct node* next; // 下一个节点的指针
}NODE,*LIST;
LIST CreateList(int); // 建立循环链表
int is_empty(LIST); // 判断循环链表是否为空
int DelItem(LIST*,NODE*); // 删除元素
int main(void)
{
// n为人数,m为报到几的出局,可以使用scanf()函数获取,这里没有这么写
// i为当前报道的数,num为出局的序号
int m = 3, n = 41, i = 0, num = 0;
LIST list = NULL;
NODE* now = NULL;
NODE* next = NULL;
// 初始化循环队列
list = CreateList(n);
// 当前指向队列头
now = list;
// 从第一个数开始报数,当队列不空
i = 1;
while (!is_empty(list))
{
// 如果这个人报到M,出局
if (i == m)
{
// 这个人的下一个人
next = now->next;
// 调用函数删除这个人
num = DelItem(&list, now);
// 输出被删除人的序号
printf("%d\n", num);
// 从1开始再次报数
i = 1;
now = next;
}
else
{
// 报数+1
i++;
//下一个人
now = now->next;
}
}
// 等待输入,vs2017需要这句暂停程序
getchar();
return 0;
}
// 建立循环链表
LIST CreateList(int n)
{
if (n == 0)
{
return NULL;
}
else
{
// 当前的节点值
int i = 1;
// 循环链表
LIST list = NULL;
// 两个工作节点
NODE* p = NULL;
NODE* q = NULL;
// 生成第一个节点
p = (node*)malloc(sizeof(node));
p->num = i;
list = p;
// 当前节点个数小于总个数
while(i < n)
{
// 节点序号+1
i++;
// 生成新节点
q = (NODE*)malloc(sizeof(NODE));
// 把新节点插入链表
q->num = i;
q->next = NULL;
p->next = q;
p = q;
}
// 设置链表为循环链表
p->next = list;
return list;
}
}
// 判断是否是空队列
int is_empty(LIST list)
{
return list == NULL;
}
//删除元素
int DelItem(LIST* plist, NODE* now)
{
if (is_empty(*plist))
{
return 0;
}
else if((*plist)->next == *plist) //如果是最后一个元素
{
int num = 0;
num = (*plist)->num;
free((*plist)->next);
*plist = NULL;
return num;
}
else
{
if ((*plist) == now) // 要删除表头元素
{
(*plist) = (*plist)->next; // 设置下一个元素为表头元素
}
// 工作节点
NODE *p = (*plist);
NODE *temp = NULL;
int num = 0;
// 查找要删除节点的前一个节点
while (p->next != now)
{
p = p->next;
}
// 删除节点
temp = p->next;
p->next = temp->next;
num = temp->num;
free(temp);
return num;
}
}