约瑟夫问题是经典的循环链表。
1)内容: 约瑟夫(Joseph)问题的一种描述是:编号为1,2,…, n 的n 个人按顺 时针方向围坐一圈, 每人持有一个密码(正整数)。一开始选任一个正整数作为报数上限值m, 从第一个人开始按顺时针方向自1开始顺序报数,报到m时停止报数。报m的人出列,将它的密码作为新的m值,再从下个人开始新一轮报数,如此反复,直到剩下最后一人则为获胜者。试设计一个程序求出出列顺序。
2)要求: 利用单向循环链表存储结构模拟此过程, 按照出列的顺序印出各人的编号。
3) 测试数据: n=7,7 个人的密码依次为:3,1,7,2,4,8,4 。m的初值为20,则正确的出列顺序应为6,1,4,7,2,3,5
#include <stdio.h>
#include <stdlib.h>
// 结构体的定义
typedef struct Person {
int order;
int password;
struct Person * next;
} Person;
Person * pHead = NULL; // 链表的第一个节点
Person * pTail = NULL; // 链表的尾部
Person * per = NULL; // 用于遍历
int n, m;
// 下面是函数的声明
void Create(); // 创建一个链表
void Joseph(int m); // 约瑟夫问题的执行函数
int main(void)
{
// n为总人数, m为初始密码
scanf("%d %d", &n, &m);
Create(); // 创建链表
per = pHead; // 将用于遍历的节点指向头节点,因为下面的这个函数使用递归写的
Joseph(n);
return 0;
}
void Create() {
// 定义一个为节点
Person * tail;
for (int i = 0; i < n; i++) {
Person * person;
person = (Person *) malloc(sizeof(Person));
person->order = i + 1;
scanf("%d", &person->password);
if (pHead == NULL) {
pHead = person;
} else {
tail->next = person;
}
tail = person;
}
// 因为是循环链表,所以必须将尾与头连接起来
tail->next = pHead;
// 将函数中的尾节点赋给全局变量中的尾节点
// 这个在后面有用
pTail = tail;
}
/*
* 因为最后最后每一个人都会报数(最后一个报数的为获胜者)
* 一次递归出列一个人,所以要需要递归n次
*/
void Joseph(int number) {
// 当number为0时结束递归
if (number == 0) {
return;
}
// 用于报数,第一个人报1所以初始值为1
int count = 1;
// p用于记录删除的节点
Person * p = NULL;
// 前面记录的为指针的用处就在这里
// 当m=1时,第一个人出列,所以就不用进行下面的循环
// 但是当m不等于1时,找到那个要出列的节点
while (count != m) {
per = per->next;
pTail = pTail->next;
count++;
}
// p为要删除的节点
p = per;
// 重置m
m = p->password;
// 从链表中删除p
pTail->next = per->next;
// 下面的是输出出列人的编号
// 因为有些ACM要求,用空格间隔,所以最后一个不能有空格
if(number == 1)
printf("%d", p->order);
else
printf("%d ", p->order);
per = per->next;
// 释放p
free(p);
// 进行下一个递归
Joseph(number - 1);
}
约瑟夫排列问题
对于给定的1,2,3,…,n中的k个数,Josephus想知道是否存在一个正整数m(m≤n),使得Josephus(n,m)排列的最后k个数恰好为事先指定的k个数。例如当n为7,k为4,指定排列的最后k个数为7,5,1,4时;由于(7,3)Josephus排列为3,6,2,7,5,1,4;所以求得m值为3。
这个约瑟夫排列问题,每个人都没有密码,只有一个初始值,当报到这个人时出列。要求找一个满足题目要求的初始值m。
#include <stdio.h>
#include <stdlib.h>
typedef struct Person {
int order;
struct Person * next;
} Person;
Person * pHead = NULL; // 链表的第一个节点
Person * pFront = NULL;
Person * per = NULL;
int n, m;
int b[100], k;
// 函数
void Create();
void Joseph(int number, int m);
int main(void)
{
scanf("%d %d", &n, &m);
int i;
int a[n];
for (i = n - m ; i < n; i++) {
scanf("%d", &a[i]);
}
for (i = 1; i <= n; i++) {
pHead = NULL;
pFront = NULL;
k = 0;
int flag = 1;
Create();
per = pHead;
Joseph(n, i);
for (int j = n - m; j < n; j++) {
if (a[j] != b[j]) {
flag = 0;
}
}
if (flag == 1) {
printf("%d", i);
return 0;
}
// printf("\n");
}
printf("0");
return 0;
}
void Create() {
Person * tail;
for (int i = 0; i < n; i++) {
Person * person;
person = (Person *) malloc(sizeof(Person));
person->order = i + 1;
if (pHead == NULL) {
pHead = person;
} else {
tail->next = person;
}
tail = person;
}
tail->next = pHead;
pFront = tail;
}
void Joseph(int number, int m) {
if (number == 0) {
return;
}
int count = 1;
Person * p = NULL;
while (count != m) {
per = per->next;
pFront = pFront->next;
count++;
}
p = per;
pFront->next = per->next;
if(number == 1) {
b[k++] = p->order;
// printf("%d", p->order);
}
else {
// printf("%d ", p->order);
b[k++] = p->order;
}
per = per->next;
free(p);
Joseph(number - 1, m);
}