前言
欢迎大家光临我的博客,本篇文章将着重介绍约瑟夫环问题的分析,算法设计以及c风格代码的基本实现。
本篇文章的约瑟夫环问题的描述
设有编号为1,2,...n的n(n>0)个人围坐成一个圈,每个人持有一个密码m,从第一个人开始报数,报到m停止,报m的人出圈,如此下去,直到所有人全部出圈为止。当任意给定n和m后,设计算法求n个人出圈的次序。
问题分析
一共有N个人,所有人围成一个闭环。开始时,选择第一个人,根据他的密码值m,从他开始(包括他),从1开始向后报数,报到m为止,此时所指向的人出圈。选择刚才出圈的人的下一个人,根据他的密码值m,从他开始(包括他),从1开始向后报数,......如此循环下去,直到全部人出圈。
不妨设p(n)表示编码为n的人,p(n).m表示这人的密码,p(n).next表示他的下一个人。
假设编码n的人向后数k后,指向编码z人,则p(n+k-1) 不一定等于 p(z),也就是 n+k-1不一定等于z。原因是,当有人出圈后,构成这个圆环的编码数将不再连续(从正整数的角度来看),也就推不出z = n+k-1的线性关系。其实,在开始前,圆环也是不连续,因为从N到1是跳跃的。
为了解决这个非线性问题,我们可以通过链表将它划分为若干个的线性问题。
如何理解链表的线性?
我们知道链表是由若干个两两关联的节点构成的,任何一个节点都清楚地知道它的上一个节点和下一个节点(这里说的是双向链表)。笔者认为这样的情景中蕴涵了一种直达关系,也就是线性。所以链表蕴涵了若干个线性关系。
所以我们可以使用链表模型来解决这个问题。
算法设计
根据问题分析的结果,我们可以写出以下伪代码:
0 定义 v = (b,m) 其中 v是b与m的组合,b表示编码,m表示密码
1 定义 S = {v1,v2,v3...end} 其中 S为链表,end是空元素,用来表示元素的终结
2 定义 R 为出圈编码的有序链表
3 令 p = S中的第一个元素
4 if S的元素个数!=0 :
5 令 n = 1
6 令 k = p的密码
7 if n < k :
8 令 n = n + 1
9 令 p = p的下一个元素
10 if p是S的end :
11 令 p = S的第一个元素
12 repeat
13 令 p的编码加入到R
14 令 tmp = p的下一个元素
15 将 p从S中移除
16 令 p = tmp
17 repeat
首先从S中选择第一个元素,在S的元素个数不等于0的前提下,不断将元素从S中移除。
第5行~第12行,实现了从1开始报数。
第9行与第10行,n ,p是同步变化,实现了数数的细节。
第11行,判断元素是否为end,如果是,将p指向S的首元素,实现闭环。
第13行,把要出圈的元素的编码添加到结果链表中。
第14行~第16行,实现元素的移除和迭代。
时间复杂度和空间复杂度
基于上述伪代码,通过观察代码结构,我们可以发现,移除S中的一个元素所需要的时间主要依赖于这个元素的密码m。我们假定每一条指令的时间复杂度相同,且为t。我们使用tn来表示第n行代码需要的时间。那么整个程序的时间复杂度可以写为:
由于我们仅使用了链表,假设链表每个元素的空间复杂度为c,那么n个元素的空间复杂度就为cn。
所以整个程序的空间复杂度为cn+k;k为其它所有变量总的复杂度,是一个常量。
建立数据模型
接下来,根据上述分析的伪代码,采用C风格的代码来实现。
1.抽象编码和密码
struct Person {
int number;
int key;
};
2.定义节点结构
struct simple_node {
Person person;
simple_node* next;
simple_node* last;
};
3.定义链表及其相关方法
struct simple_list {
simple_node* finish;
int count;
};
//初始化链表
void init_simple_list(simple_list* lplist) {
lplist->finish = (simple_node*)malloc(sizeof(simple_node));
//链表闭环
lplist->finish->next = lplist->finish;
lplist->finish->last = lplist->finish;
lplist->count = 0;
}
//销毁链表
void destory_simple_list(simple_list* lplist) {
for (simple_node* node = lplist->finish->next; node != lplist->finish; node = lplist->finish->next) {
//解除 node 与 链表的联系
lplist->finish->next = node->next;
node->next->last = lplist->finish;
//回收节点
free(node);
}
//回收终止节点
free(lplist->finish);
lplist->count = 0;
}
//在指定位置添加元素
void simple_list_insert(simple_list* lplist, simple_node* pos, Person* lpPerson) {
simple_node* targ_node = pos;
//分配新内存
simple_node* new_node = (simple_node*)malloc(sizeof(simple_node));
//构造该节点的数据
new_node->person.key = lpPerson->key;
new_node->person.number = lpPerson->number;
//将新节点接入链表,默认插入在pos的前面
new_node->next = targ_node;
new_node->last = targ_node->last;
targ_node->last->next = new_node;
targ_node->last = new_node;
//维护count
lplist->count++;
}
//删除指定位置的元素
void simple_list_erase(simple_list* lplist, simple_node* pos) {
simple_node* targ_node = pos;
//接触该位置的节点与链表的联系
targ_node->last->next = targ_node->next;
targ_node->next->last = targ_node->last;
//回收该节点
free(targ_node);
//维护count
lplist->count--;
}
算法实现
int main()
{
simple_list personlist;
simple_list resultlist;
init_simple_list(&personlist);
init_simple_list(&resultlist);
int recive = 0;
int n = 0;
Person person;
while (scanf_s("%d", &recive)) {
n++;
person.number = n;
person.key = recive;
simple_list_insert(&personlist, personlist.finish, &person);
}
simple_node* node = personlist.finish->next;
while (personlist.count != 0) {
int key = node->person.key;
int n = 1;
while (n < key) {
n++;
node = node->next;
if (node == personlist.finish) //因为终止节点没有代表任何人,所以因该迭代到下一个节点,形成逻辑闭环
node = node->next;
}
//将结果添加到结果列表中
simple_list_insert(&resultlist, resultlist.finish, &(node->person));
//删除该节点并向后迭代
simple_node* tmp = node;
node = node->next;
if (node == personlist.finish)
node = node->next;
simple_list_erase(&personlist,tmp);
}
//输出结果
for (simple_node* node = resultlist.finish->next; node != resultlist.finish; node = node->next)
printf("%d ", node->person.number);
//一不要忘记回收数据
destory_simple_list(&personlist);
destory_simple_list(&resultlist);
}
完整代码
#include<stdio.h>
#include<stdlib.h>
struct Person {
int number;
int key;
};
struct simple_node {
Person person;
simple_node* next;
simple_node* last;
};
struct simple_list {
simple_node* finish;
int count;
};
void init_simple_list(simple_list* lplist) {
lplist->finish = (simple_node*)malloc(sizeof(simple_node));
//链表闭环
lplist->finish->next = lplist->finish;
lplist->finish->last = lplist->finish;
lplist->count = 0;
}
void destory_simple_list(simple_list* lplist) {
for (simple_node* node = lplist->finish->next; node != lplist->finish; node = lplist->finish->next) {
//解除 node 与 链表的联系
lplist->finish->next = node->next;
node->next->last = lplist->finish;
//回收节点
free(node);
}
//回收终止节点
free(lplist->finish);
lplist->count = 0;
}
void simple_list_insert(simple_list* lplist, simple_node* pos, Person* lpPerson) {
simple_node* targ_node = pos;
//分配新内存
simple_node* new_node = (simple_node*)malloc(sizeof(simple_node));
//构造该节点的数据
new_node->person.key = lpPerson->key;
new_node->person.number = lpPerson->number;
//将新节点接入链表,默认插入在pos的前面
new_node->next = targ_node;
new_node->last = targ_node->last;
targ_node->last->next = new_node;
targ_node->last = new_node;
//维护count
lplist->count++;
}
void simple_list_erase(simple_list* lplist, simple_node* pos) {
simple_node* targ_node = pos;
//接触该位置的节点与链表的联系
targ_node->last->next = targ_node->next;
targ_node->next->last = targ_node->last;
//回收该节点
free(targ_node);
//维护count
lplist->count--;
}
int main()
{
simple_list personlist;
simple_list resultlist;
init_simple_list(&personlist);
init_simple_list(&resultlist);
int recive = 0;
int n = 0;
Person person;
while (scanf_s("%d", &recive)) {
n++;
person.number = n;
person.key = recive;
simple_list_insert(&personlist, personlist.finish, &person);
}
simple_node* node = personlist.finish->next;
while (personlist.count != 0) {
int key = node->person.key;
int n = 1;
while (n < key) {
n++;
node = node->next;
if (node == personlist.finish) //因为终止节点没有代表任何人,所以因该迭代到下一个节点,形成逻辑闭环
node = node->next;
}
//将结果添加到结果列表中
simple_list_insert(&resultlist, resultlist.finish, &(node->person));
//删除该节点并向后迭代
simple_node* tmp = node;
node = node->next;
if (node == personlist.finish)
node = node->next;
simple_list_erase(&personlist,tmp);
}
//输出结果
for (simple_node* node = resultlist.finish->next; node != resultlist.finish; node = node->next)
printf("%d ", node->person.number);
//一不要忘记回收数据
destory_simple_list(&personlist);
destory_simple_list(&resultlist);
}
结语
最后,感谢大家阅读我的博客,如果这篇文章对你有帮助,请不要忘了为我点赞,你的鼓励是我持续创作的动力!