查找數組中第k大的數


  問題:  查找出一給定數組中第k大的數。例如[3,2,7,1,8,9,6,5,4],第1大的數是9,第2大的數是8……

思考:1. 直接從大到小排序,排好序后,第k大的數就是arr[k-1]。 

2. 只需找到第k大的數,不必把所有的數排好序。我們借助速排序中partition過程,一般情況下,在把所有數都排好序前,就可以找到第k大的數。我們依據的邏輯是,經過一次partition后,數組被pivot分成左右兩部分:S左、S右。當S左的元素個數|S左|等於k-1時,pivot即是所找的數;當|S左|小於k-1,所找的數位於S右中;當|S左|>k-1,所找的數位於S左中。顯然,后兩種情況都會使搜索空間縮小。

算法的時間復雜度為:O(N)--詳情參考算法導論。

#include<iostream>
using namespace std;
int Partition(int a[], int i, int j)
{
  int tmp = a[j];
  int index =i;
  if (i < j)
  {
    for (int k=i;k<j;k++){
      if(a[k] >= tmp){
        swap(a[index++],a[k]);
      }
    }
    swap(a[index],a[j]);
    return index;
  }
}
int Search(int a[], int i, int j, int k)
{
    int m = Partition(a, i, j);
    if (k==m-i+1) return a[m];
    else if (k<m-i+1)
    {
      return Search(a, i, m-1,k );
    }
//后半段
    else
    {
      return Search(a, m+1, j, k-(m-i+1));
    }
}
int main()
{
    int a[7] = { 8,7,6,1,2,3,4 };
    int k = 3;
    cout << Search(a,2, 6, k);
}

 

  上述問題對應於尋找前K大個數。上述方法對應的數據量比較小,如果N很大,100億?甚至更多,這個時候數據不能夠全部放入內存,所以要求盡可能少遍歷數據。不妨設N>K,考慮前K個數中的最大K個數的一個退化的情況:所有K個數就是最大的K個數。如果考慮第K+1個數X呢?如果X比最大的K個數中的最小的數Y小,則最大的K個數保持不變。如果X比最大的K個數中個最小的數Y大,則最大的K個數要除去Y,加入X。如果用一個數組來保存前K大的數,每加入一個數X,就掃描一遍數組。得到數組中最小的數Y,用X代替Y或者保持不變。這種方法消耗的時間O(N*K)

  進一步,可以用容量為K的最小堆來存儲最大的K個數。最小堆的堆頂元素就是K個數中最小的一個。每次考慮一個數X,如果X比堆頂元素Y小,則保持最小堆不變,因為這個元素比最大的K個數小。如果X

比堆頂元素Y大,那么用X替換原來的堆頂元素Y,X可能破壞原來的最小堆結構(每個結點比它的父節點大),需要更新堆來維持堆的性質。更新堆時間復雜度為O(log2K).總的算法復雜度為O(N*log2k)

 1 #include <iostream>
 2 using namespace std;
 3 //調整堆
 4 void HeapAdjust(int a[],int i,int size)
 5 {
 6     int left = 2 * i + 1;
 7     int right = 2 * i + 2;
 8     int min = i;
 9     if (left < size&&a[left] < a[min])
10             min = left;
11     if (right < size&&a[right] < a[min])
12         min = right;
13         if (min != i)
14         {
15             int temp = a[min];
16             a[min] = a[i];
17             a[i] = temp;
18             HeapAdjust(a,min,size); //避免調整之后以min為父節點的子樹不是堆
19         }
20 }
21 //建立堆
22 void HeapBuild(int a[],int size)
23 {
24     for (int i = size / 2 - 1; i >= 0; i--)
25         HeapAdjust(a,i,size);
26 }
27 //k為需要查找的最大元素個數,size為數組大小,kMax存儲k個元素的最小堆  
28 void FindMax(int Array[], int k, int size, int kMax[])
29 {
30     for (int i = 0; i < k; i++)
31         kMax[i] = Array[i];
32     HeapBuild(kMax,k);
33     for (int j = k; j < size; j++)
34     {
35         if (Array[j] <= kMax[0]) continue;
36         kMax[0] = Array[j];
37         HeapAdjust(kMax,0,k);
38     }
39 }
40 int main()
41 {
42     int a[] = {10,23,17,8,52,35,7,1,28};
43     int k = 4;
44     int KMax[4] = {0};
45     FindMax(a,k,9,KMax);
46     for (int i = 0; i < k; i++)
47         cout << KMax[i] << endl;
48 }

 


免責聲明!

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



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