约瑟夫环问题即多种解法

问题引言:

约瑟夫问题是个有名的问题:N个人围成一圈,从第一个开始报数,第M个将被杀掉,最后剩下一个,其余人都将被杀掉。例如N=6,M=5,被杀掉的顺序是:5,4,6,2,3,1。

首先,当学习过约瑟夫环问题后,我向大家推荐一道力扣上的题目圆圈中最后剩下的数字,大家可以拿这道题来练练手。

力扣上的题目描述:

0,1,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字。求出这个圆圈里剩下的最后一个数字。

例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。
在这里插入图片描述

题解1:(队列模拟)

首先我会想到那一个容器来模拟约瑟夫环的整个过程,我首先想到的就是队列queue,当然大家也可以用数组,链表等等,这些甚至比队列消耗时间更少。因为我觉得队列能更形象地模拟整个过程,让读者读起来更清晰。
首先我们要把1-n-1个数依次拉入队列,然后用一个计数器sum,当sum是m总倍数时,就直接把当前数删除,否则,就把当前数拿出来放到队列最后面。直到队列中只剩下一个数字,该数字就是结果。

代码:

class Solution {
public:
    int lastRemaining(int n, int m) {
        queue<int> Q;
        int i,sum=1;
        for(i=0;i<n;i++)
        {
            Q.push(i);
        }
        while(Q.size()>1)
        {
            if(sum%m==0)
            {
                Q.pop();
            }
            else
            {
                int temp=Q.front();
                Q.pop();
                Q.push(temp);
            }
            sum++;
        }
        return Q.front();
    }
};

题解2:(递归求解)

递归求解的只要过程就是1.寻找递归出口,2.减小数据规模,3.当前规模和减小规模后的联系。
而此题前两个是比较容易实现的,递归出口无非就是n=1时,直接返回当前数字;而减小数据规模就是让n依次减1.比较难的就是找出减小规模前后之间的联系。
那么f(n-1,m)和f(n,m)到底有什么关系呢,x=f(n-1,m),说明当人数为n时,存活下来的人的编号为x。x代表什么呢,就是从“0”(开始)数x+1下即为结果。
而当前有n+1个人,首先拉出去的就是(m-1)%n,然后就变成了n个人了。但是我们现在不能再从真实的0开始数,我们要接着数,所以此时的“0”即为(m-1)%n。所以现在的结果就是((m-1)%n+x+1)%n,即为(m+x)%n.

代码:

class Solution {
public:
    int f(int n,int m)
    {
        if(n==1)
        {
            return 0;
        }
        int x=f(n-1,m);
        return (m+x)%n;
    }
    int lastRemaining(int n, int m) {
        return f(n,m);
    }
};

题解3:(递归的非递归实现)

其实本质上就是相对于递归,每个递归都能转换为非递归。有的需要借助于栈,而有的只需要利用数学方法进行迭代就能实现。
此题就是从n=2反推,从n=2然后逐步增加n的大小,直到求出答案。

class Solution {
public:
    int lastRemaining(int n, int m) {
        int f = 0;
        for (int i = 2; i != n + 1; ++i)
            f = (m + f) % i;
        return f;
    }
};
发布了39 篇原创文章 · 获赞 22 · 访问量 1150

猜你喜欢

转载自blog.csdn.net/M_L_Fighting/article/details/105195759