约瑟夫环问题
#约瑟夫环问题
约瑟夫环对于学过算法的应该很熟悉了吧,其问题描述一般是如下情景:
* N个人围成一圈,从第一个开始报数,第M个将被杀掉,最后剩下一个,其余人都将被杀掉。例如N=6,M=5,被杀掉的顺序是:5,4,6,2,3,求最后剩下的人的编号
解决这个问题也很容易想到循环链表,我们只需要创建一个无头节点的循环链表,然后从第一个开始计数。每移动一个节点索引加一,直到索引等于M时移除该节点,然后将索引重置为1,循环至仅剩一个节点即可
代码实现:
typedef struct node {
int data;
node* next;
} node;
void create(node*& L, node*& r, int n) {
L = new node;
L->data = 1;
r = L;
for (int i = 1; i < n; i++) {
node* s = new node;
s->data = i + 1;
r->next = s;
r = s;
}
r->next = L;
}
void out(node*& p, node*& pre, int k, int m) {
while (--k) {
p = p->next;
pre = pre->next;
}
int i = 1;
while (p->next != p) {
if (i == m) {
i = 1;
cout << p->data << " ";
pre->next = p->next;
delete p;
p = pre->next;
} else {
pre = pre->next;
p = p->next;
++i;
}
}
cout << p->data << endl;
}
但这太复杂了不是吗
我们不仅要手搓一个循环链表,还容易访问超界(我第一次写就这样)
所以我们有一个更简单的方式解决这个问题:递归实现
先看数学公式:
f(N,M)=(f(N−1,M)+M)%n
为了理解这个公式,我们先假设有n个人,编号分别从0到n-1:
0, 1, 2, 3, 4, 5, 6 … (M-1)%n, M%n, (M+1)%n … n-2, n-1
显然,第(M-1)%n个是第一个嗝屁的
这时可能有人会想:我们为什么要用%n来表示编号呢?直接写M-1不是一样的吗?
很简单,我们去掉那个第一个嗝屁的倒霉蛋之后,从他的下一个开始数,就可以得到一个新的序列:
M%n, (M+1)%n, (M+2)%n … n-1, 0, 1, 2, 3 … (M-2)%n
那么对他们重新编号后就可以得:
0, 1, 2 ………….. n-2
相互对比后,我们可以很容易根据%n判断出上面的序列是在n个人时排的,下面编号n_2与上面编号n_1的对应关系便是:
n_1 = (n_2 + M)%n
这就是上面的序号为什么要加一个%n
代码实现:
int cir(int n,int M)
{
int p=0;
for(int i=2;i<=n;i++)
{
p=(p+M)%i;
}
return p+1;
}
这样就解决了
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Zzrty's site!

