圓圈中最後剩下的數字 ----《劍指offer》面試題45

題目

從撲克牌中隨機抽5張牌,判斷是不是一個順子,即這5張牌是不是連續的。2~10爲數字本身,A爲1,J爲11,Q爲12,K爲13,而大、小王可以看成任意數字。

思路一

用模板庫中的std::list來模擬一個環形鏈表。由於std::list本身不是一個環形結構,因此每當迭代器掃描到末尾的時候,我們要記得把迭代器移動到鏈表的頭部,這樣就相當於在一個圓圈裏遍歷了。

時間複雜度:O(n * m)
空間複雜度:O(n)

思路二

定義一個關於n和m的方程f(n,m),表示每次在n個數字0,1,…,n-1中每次刪除第m個數字最後剩下的數字。
f(n,m)={0n=1 [f(n1,m)+m]n f(n,m)=\begin{cases} 0 n=1 \\ [f(n-1,m) + m] % n >1\\ \end{cases}
時間複雜度:O(n)
空間複雜度:O(1)

代碼

#include <iostream>
#include <list>

using namespace std;


//  用list<int>實現約瑟夫環,裏面存放0~n-1 這n個數,每次刪除第m個數
//  返回最後的一個數字
int LastRemaining_A(unsigned n, unsigned m)
{
    if (n < 1 || m < 1)
        return -1;

    unsigned i = 0;
    list<int> numbers;

    for (i = 0; i < n; ++i)
        numbers.push_back(i);

    list<int>::iterator current = numbers.begin();
    while (numbers.size() > 1)
    {
        //  尋找第m個數
        for (int i = 1; i < m; ++i)
        {
            current++;
            if (current == numbers.end())   // 如果到達尾部,則跳轉到開頭
                current = numbers.begin();
        }

        //  由於list<int>刪除元素時,會使後面迭代器失效
        //  則在刪除當前元素前,先保存下一個迭代器next
        list<int>::iterator next = ++current;
        if (next == numbers.end())
            next = numbers.begin();

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

    return *(current);
}

//  利用數學公式解決瑟夫環問題
//  f(n,m)表示從0~n-1 這n個數,刪除第m個數後,下次開始的下標
//  n=1 : f(n,m) = 0
//  n>1 : f(n,m) = [f(n-1,m) + m] % n
int LastRemaining_B(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 main()
{
    int n = 5;
    int m = 3;
    cout << "the result of method A:" << LastRemaining_A(n, m) << endl;
    cout << "the result of method B:" << LastRemaining_B(n, m) << endl;

    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章