(劍指Offer)面試題45:圓圈中最后剩下的數字


題目:

0,1,...n-1這n個數字排成一個圓圈,從數字0開始每次從這個圓圈里刪除第m個數字,求出這個圓圈里剩下的最后一個數字。

思路:

1、環形鏈表模擬圓圈

創建一個n個節點的環形鏈表,然后每次在這個鏈表中刪除第m個節點;

可以用std::list來模擬環形鏈表,list本身不是環形結構,因此每當迭代器掃描到鏈表末尾的時候,需要將迭代器移到鏈表的頭部。

2、分析每次被刪除的數字的規律,動態規划

假設從0-n-1中刪除了第m個數字,則下一輪的數字排列為m,m+1,.....n,1,2,3...m-2,將該數字排列重新映射為0~n-2,則為

m    0

m+1    1  

....    ....

n-1   n-1-m

0    n-m

1    n-m+1

...    ....

m-2    n-2

可以看出從右往左的映射關系為left=(right+m)%n,即0~n-1序列中最后剩下的數字等於(0~n-2序列中最后剩下的數字+m)%n,很明顯當n=1時,只有一個數,那么剩下的數字就是0.

問題轉化為動態規划問題,關系表示為:

f(n)=(f(n-1)+m)%n; 當n=1,f(1)=0;

代碼:

1、環形鏈表

#include <iostream>
#include <list>
using namespace std;

int lastRemaining(unsigned int n,unsigned int m){
    if(n<1 || m<1)
        return  -1;

    std::list<int> numbers;
    for(unsigned int i=0;i<n;i++)
        numbers.push_back(i);

    std::list<int>::iterator current=numbers.begin();
    std::list<int>::iterator next;
    while(numbers.size()>1){
        for(unsigned int i=1;i<m;i++){
            current++;
            if(current==numbers.end())
                current=numbers.begin();
        }
        next=++current;
        if(next==numbers.end())
            next=numbers.begin();

        --current;
        numbers.erase(current);
        current=next;
    }
    return *(current);
}

int main()
{
    cout << lastRemaining(5,3) << endl;
    return 0;
}

2、映射規律,動態規划

int lastRemaining_1(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;
}


int lastRemaining_2(unsigned int n,unsigned int m){
    if(n<1 || m<1)
        return  -1;
    if(n==1)
        return 0;
    return (lastRemaining_2(n-1,m)+m)%n;
}

在線測試OJ:

http://www.nowcoder.com/books/coding-interviews/f78a359491e64a50bce2d89cff857eb6?rp=2

AC代碼:

1、環形鏈表

class Solution {
public:
    int LastRemaining_Solution(unsigned int n, unsigned int m)
    {
    	if(n<1 || m<1)
            return -1;

        std::list<int> numbers;
        for(unsigned int i=0;i<n;i++)
            numbers.push_back(i);

        std::list<int>::iterator current=numbers.begin();
        std::list<int>::iterator next;

        while(numbers.size()>1){
            for(unsigned int i=1;i<m;i++){
                ++current;
                if(current==numbers.end())
                    current=numbers.begin();
            }

            next=++current;
            if(next==numbers.end())
                next=numbers.begin();

            --current;
            numbers.erase(current);
            current=next;
        }

        return *current;
    }
};

2、序列規律,動態規划

class Solution {
public:
    int LastRemaining_Solution(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;
    }
};

class Solution {
public:
    int LastRemaining_Solution(unsigned int n, unsigned int m)
    {
        if(n<1 || m<1)
            return -1;
        if(n==1)
            return 0;
        
        return (LastRemaining_Solution(n-1,m)+m)%n;
    }
};


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM