劍指offer (30) 最小的K個數

來源:互聯網
上載者:User

標籤:style   class   blog   code   http   color   

題目:輸入n個整數,找出其中最小的K個數

方法一:直接std::sort,T(n) = O(nlgn)

方法二:直接std::nth_element T(n) = O(n) 但是修改了原數組

void MinKth(std::vector<int>& num, int kth, std::vector<int>& result){    if (num.size() == 0 || || kth <= 0 || kth > num.size()) {        throw std::invalid_argument("invalid_argument");    }    std::nth_element(num.begin(), num.begin() + kth - 1, num.end());    for (int i = 0; i < kth; ++i) {        result.at(i) = num.at(i);    }}

 

自己實現 nth_element:

一次劃分:

int Partition(std::vector<int>& num, int first, int last){    if (first < 0 || last >= num.size() || first > last) {        throw std::invalid_argument("invalid_argument");    }    int index = rand() % (last - first + 1) + first;    int key = num.at(index);    std::swap(num.at(first), key);    int midIndex = first;    for (int i = first + 1; i <= last; ++i) {        if (num.at(i) < num.at(first)) {            std::swap(num.at(++midIndex), num.at(i));        }    }    std::swap(num.at(midIndex), num.at(first));    return midIndex;}

 

void GetKth(std::vector<int>& num, int kth, std::vector<int>& result){    if (num.size() == 0 || kth > num.size()) {        throw std::invalid_argument("invalid_argument");    }    int first = 0;    int last = num.size() - 1;    int index = Partition(num, first, last);    while (index != kth - 1) {        if (index > kth - 1) {            last = index - 1;            index = Partition(num, first, last);        } else {            first = index + 1;            index = Partition(num, first, last);        }    }    for (int i = 0; i < kth; ++i) {        result.at(i) = num.at(i);    }}

 

方法三:O(nlgk)解法

先建立一個大小為K的資料容器來儲存最小的k個數,然後順序遍曆原數組:

如果容器中已有數字少於K個,則直接將原數組元素讀入到容器中

如果容器中已有K個數字,表示已滿,這時 我們找出容器中K個數的最大值,將這個最大值與待插入元素比較:

如果待插入元素比最大值還要大,那麼直接忽略

如果待插入元素比最大值要小,那麼就用這個元素替換掉最大值

 

我們該選取什麼資料結構來表示這個資料容器呢?

如果是vector數組,那麼每次找最大值需要O(k)時間,總時間為O(nk)

注意到我們每次僅僅需要的是最大值,很自然地想到最大堆,每次調整堆需要O(lgk),總時間為O(nlgk)

 

(1) 資料容器使用vector

void GetKth(const std::vector<int>& num, int kth, std::vector<int>& result){    if (num.size() == 0 || kth <= 0 || kth > num.size()) {        throw std::invalid_argument("invalid_argument");    }    for (int i = 0; i < num.size(); ++i) {        if (result.size() < kth) {            result.push_back(num.at(i));        } else {            auto maxIter = std::max_element(result.begin(), result.end());            if (num.at(i) < *maxIter) {                *maxIter = num.at(i);            }        }    }}

 

(2)資料容器使用 堆

首先 堆是一棵完全二叉樹,根據完全二叉樹的性質,我們可以非常方便的由根節點定位到左子節點和右子節點

一般使用數組來表示完全二叉樹,結點之間的關係依靠 結點編號來維持

假設父節點的編號為 index (1<= index <= num.size() ) 其左子節點編號為 2*index, 其右子節點編號為 2*index + 1

(註:圖片來自於:http://www.cnblogs.com/Anker/archive/2013/01/23/2873422.html)

(1) 堆調整

堆調整操作是對編號為index的結點進行“下降”,使以index為根的子樹成為最大堆  

(註:圖片來自於:http://www.cnblogs.com/Anker/archive/2013/01/23/2873422.html)

 

void MaxHeapAdjust(std::vector<int>& num, int index) {    int leftIndex = 2 * index;    int rightIndex = 2 * index + 1;    int largest = 0;    if (leftIndex <= num.size()) {        if (num.at(leftIndex - 1) > num.at(index - 1)) {            largest = leftIndex;        } else {            largest = index;        }    }    if (rightIndex <= num.size()) {        if (num.at(rightIndex - 1) > num.at(largest - 1)) {            largest = rightIndex;        }    }    if (largest == 0) return;    if (largest != index) {        std::swap(num.at(index - 1), num.at(largest - 1));        MaxHeapAdjust(num, largest);    }}

 

(2) 建堆

建立最大堆的過程是自底向上地調用最大堆調整程式將一個數組A[1.....N]變成一個最大堆。將數組視為一顆完全二叉樹,從其最後一個非葉子節點(n/2)開始進行堆調整

for (int i = num.size() / 2; i >= 1 ; --i) {     MaxHeapAdjust(num, i);}

 

堆調整和建堆操作都已經說清楚了,再回到本題中來

我們首先取K個數建堆,然後對其後的數字不斷與堆頂元素(即最大值)做比較,如果比堆頂元素要小,即更新堆頂元素,並以堆頂元素為根結點進行堆調整操作

 

void GetKth(const std::vector<int>& num, int kth, std::vector<int>& heap) {    if (num.size() == 0 || kth <= 0 || kth > num.size()) {        throw std::invalid_argument("invalid_argument");    }        for (int i = 0; i < kth; ++i) {        heap.push_back(num.at(i));    }    for (int i = heap.size() / 2; i >= 1 ; --i) {        MaxHeapAdjust(heap, i);    }        for (int i = kth; i < num.size(); ++i) {        if (num.at(i) < heap.at(0)) {            heap.at(0) = num.at(i);            MaxHeapAdjust(heap, 1);        }    }}

 

最後上一張圖總結一下兩種方法的優缺點:

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.