剑指offer-面试题45:圆圈中最后的数字

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

思路(1):用环形链表模拟圆圈。

int LastRemaining(unsigned int n, unsigned int m)
{
    if(n < 1 || m < 1)
        return -1;
        
    unsigned int i = 0;
    list<int> numbers;
    for(i = 0; i < n; ++i)
        numbers.push_back(i);
        
    list<int>::iterator it = numbers.begin();
    while(numbers.size() > 1) 
    {  
        for(int i = 1; i < m; ++i)
        {
            ++it;
            if(it == numbers.end())
                it = numbers.begin();
        }
        list<int>::iterator tmp = ++it;
        if(tmp == numbers.end())
                tmp = numbers.begin();
        numbers.erase(--it);
        it = tmp;
    }
    
    return *it; 
}
(2)分析每次被删除的数字的规律直接计算出圆圈中最后剩下的数字。设有n个数字,要删除第m个数字,用f(n,m)表示最后一个数字,设它在序列中的坐标为s。删除第m个数字以后,序列中元素的坐标都发生了转换,原先第m个数字后面的一位下标变为0,后两位下标变为1…,坐标的转换关系为(s-m)%n。那么在删除之前,它在序列中的位置应该为(s+m)%n。删除一个元素之后求最后一个元素的过程同样也可以用函数f表示,f(n-1,m)还原到有n个元素时的坐标就是(f(n-1,m)+m)%n,所以有f(n, m) = (f(n-1,m)+m)%n,这样就得到删除前后两次的递推公式,我们不必真的去删除第m个元素。但n=1时,只有一个元素0,显然它也是最后一个元素。求解f(n, m)可以用递归的方法,也可以用循环的方法,我们可以用求解斐波那契数列类似的方法从n = 1是逐步增加n计算,避免了额外的空间消耗。

int LastRemaining(unsigned int n, unsigned int m)
{
    if(n < 1 || m < 1)
        return -1;
        
    int last = 0;
    for(int i = 2; i <= n; ++i)
        last = (last + m) % i;
        
    return last;
}


你可能感兴趣的:(剑指offer-面试题45:圆圈中最后的数字)