您的位置:首页 > 理论基础 > 数据结构算法

数据结构考研复习--线性表3(约瑟夫环)

2015-10-17 16:48 253 查看
约瑟夫环这个在一开始看的时候是一个相当蛋疼的问题,本节将为大家讲述约约瑟夫环利用循环链表以及递归来进行求解

**约瑟夫环问题的原来描述为,设有编号为1,2,……,n的n(n>0)个人围成一个圈,从第1个人开始报数,报到m时停止报数,报m的人出圈,再从他的下一个人起重新报数,报到m时停止报数,报m的出圈,……,如此下去,直到所有人全部出圈为止。当任意给定n和m后,设计算法求n个人出圈的次序。 稍微简化一下。

问题描述:n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数。求胜利者的编号。

思路:容易想到的就是用环链表来做,构建一个环链表,每个结点的编号为0, 1, …… n-1。每次从当前位置向前移动m-1步,然后删除这个结点。最后剩下的结点就是胜利者。**

我们先来非递归的算法

void JosephusProblem_Solution1(int n, int m)    //n个人,每m退出
{
int  i = 0;
LinkList *pm;
LinkList *pcur;
LinkList *head = (LinkList *)malloc(sizeof(LinkList));
LinkList *plast;

pcur = head;
pcur->next = head;
head->data = 0;

//构造循环链表
for (i = 1; i <10;i++)
{
pm = (LinkList *)malloc(sizeof(LinkList));
pm->data = i;
pcur->next = pm;
pcur = pm;
}
pcur->next = head;

//over
pcur = head;

while(pcur != pcur->next)
{
//前进m-1步
for (i = 0;i<m-1;i++)
{
plast = pcur;
pcur = pcur->next;
}
//删除
plast->next = pcur->next;
free(pcur);
pcur = plast->next;
}

printf("%d ",pcur->next->data);
}


这样的效率显然不是最高的,时间复杂度o(mn),

接下来看递归算法

公里公道的说,这个递归算法真不好想。

我们先反过来想,如果只有一个人,那么他一定就是我们要求的,如果有两个人,那么他一定跟如果只有一个人的那种情况有关,通过只有一个人我们一定会得到一些线索,同理,我们算n个人的约瑟夫环,必然跟n-1个人的约瑟夫环有关,……一定跟2个人的有关,一定跟一个人的有关。那么到底有什么规律呢?这是我们即将要讨论的问题

我们假设m = 8 , n = 3

1>>很显然第一个被删除的数是(m-1)%n

2>>假设第二轮开始的数是K,那么只有n-1个人

K -------> 0
K+1------> 1
K+2------> 2    //假设最终他是胜利者
...
k-2-----> n-2


那么我就知道,这n-1个人,如果说我们现在知道了胜利者,他的编号是X,那么说他在链表中一开始是(k+x)%n,

其中, k = m%n,带入式子我们知道其实对于n个人,胜利者的编号是(X+m)%n

还记得我们的X是什么吗???—>n-1个人时的胜利者

那么简而言之,要求n个人最终的胜利者,我们只需要知道n-1个人时候的情况就行了,同理要知道n-1个人必须先求出n-2个人………..必须先求出1个人的情况,一个人编号自然是0,转化成代码就是

fun(1) = 0;
fun(n) = ( fun(n-1) + m ) %n;


int JosephusProblem_Solution2(int n, int m )
{
if (n==1)
{
return 0;
}
return (JosephusProblem_Solution2(n-1, m) + m) % n;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: