廣州4399面試題(一)

凝霜發表於2013-03-31

廣州4399面試題(一)

By 馬冬亮(凝霜  Loki)

一個人的戰爭(http://blog.csdn.net/MDL13412)

題目描述

現給定一個含有n個元素的陣列,請隨機獲取其中的m個元素(不能重複獲取)。

演算法描述

首先,隨機獲取元素,可以使用rand() % 陣列長度

其次,要保證元素的不重複獲取,只需將獲取的元素從原陣列中移除即可,但是每次都進行刪除操作,需要頻繁的移動陣列元素,其複雜度很高;現在,我們換一種思路,將獲取的元素與原陣列最後的元素進行交換,再將陣列的長度減一,那麼就可以做到O(1)複雜度將其移除;詳見下圖:




















此演算法的時間複雜度為O(n),其只與要獲取的m個數有關,在n和m相差非常懸殊的時候,效率非常高。

程式原始碼

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>

using namespace std;

typedef int ErrorType;

const ErrorType ErrSucceed          =   0;
const ErrorType ErrInvalidRange     =   10;
const ErrorType ErrUnknown          =   100;

template <typename ItemType>
ErrorType GetRandItemsFromArray(vector<ItemType> &items,
                                const size_t itemCount, 
                                vector<ItemType> &result)
{
    // 邊界檢測
    if (items.size() < itemCount)
        return ErrInvalidRange;

    // 將結果向量清空,並預留足夠的空間,防止多次分配導致的效能開銷。
    result.clear();
    result.reserve(itemCount);

    // 注意差1的邊界錯誤
    size_t itemsLength = items.size() - 1;

    // 演算法描述見正文
    for (size_t i = 0; i < itemCount; ++i)
    {
        int tmpIndex = rand() % itemsLength;
        result.push_back(items[tmpIndex]);
        swap(items[tmpIndex], items[itemsLength]);
        --itemsLength;
    }

    return ErrSucceed;
}

int main()
{
    vector<int> vec;
    for (int i = 0; i < 100; ++i)
        vec.push_back(i);
    vector<int> res;
    for (int i = 0; i < 10; ++i)
    {
        GetRandItemsFromArray(vec, 10, res);
        copy(res.begin(), res.end(), ostream_iterator<int>(cout, " "));
        cout << endl;
    }

    return 0;
}

總結

題目本身不難,但是要注意邊界的校驗,錯誤處理,程式碼可讀性,資料抽象,時間及空間複雜度。另外,如果是現場寫程式碼,最好問面試官原始陣列是否允許修改!

相關文章