優先隊列的應用:求序列第 k 個最大的元素

優先隊列【堆】的應用:ios

    選擇問題:輸入時N 個元素以及一個整數k ,這N 個元素的集能夠是全序的。該選擇問題是要找出第 k 個最大的元素。。。算法

    有多種解決方案:數組

1A算法:把這些元素讀入數組,並對其進行排序,返回適當的元素。假設使用的是簡單排序算法,時間複雜度是O(N^2)。ui

1B算法:將k 個元素讀入一個數組,並對其進行排序。這些元素的最小者在第k 個位置上,一個一個的處理其他的元素。當一個元素開始被處理時,它先與第k 個元素進行比較,若是該元素大,則將第k 個元素出去,該元素放在其他k - 1 個排好序的元素中的正確位置。當算法結束時,第k 個位置上的元素就是該問題的解答。該算法的運行時間爲O(N*k),若是k = N / 2(上取整),則運行時間爲O(N^2)【此時的 k 爲中位數】。spa

2A算法:將N個元素讀入一個數組,而後對該數組創建一個大頂堆。最後,執行k 次刪除最大值的操做。從該堆最後提取的元素就是咱們的答案。若是使用 BuildHeap 算法構造堆的最壞狀況是 O(N) 【具體分析參見文章(BuildHeap 構建堆的運行時間的界)】,每次刪除操做用時O(logN),因爲k 次刪除,所以,獲得總的運行時間是O(N + k * logN), 若k = N / 2(上取整),那麼運行時間爲O(NlogN) 【堆排序】。.net

2A-改進:在任意時刻都維持k 個最大元素結合S, 在前 k 個元素讀入之後,當再讀入一個新的元素時,該元素將與第 k 個最大的元素進行比較,記這第k 大的元素爲 Sk,注意 Sk 是 S 中的最小元素。若是新元素更大,則用新元素替代這個 Sk 。此時 S 中將有一個新的最小元。這裏,咱們使用一個堆來實現 S 。前 k 個元素經過堆的創建過程,創建一個堆,總時間爲 O(k),處理其他的每一個元素的時間爲 O(1) (檢測元素是否進入堆),在加上時間O(log k)(在必要時刪除 Sk, 並插入新元素)。所以,總的時間是O(k + (N - k) * log K) = O(N log k)。若 k 爲中位數,則時間爲 O(NlogN)。【代碼以下:】blog

//利用堆尋找某一序列中第k大的元素(從小到大)即第k大的元素
#include<iostream>
#define MINHEAPSIZE 1
#define MINVALUE 0.0001
using namespace std;
排序

typedef int ElemType;
typedef struct HeapStruct
{
 ElemType *elem;
 int Size;
 int Capasity;
}Heap, *PriorityQueue;
隊列

PriorityQueue InitializeHeap(int MaxSize)    //堆的初始化
{
 if(MaxSize < MINHEAPSIZE)
  throw exception("The priority queue is too small!");
 PriorityQueue H;
 H = new HeapStruct();
 H->Size = 0;
 H->Capasity = MaxSize;
 H->elem = new ElemType[MaxSize + 1];
 H->elem[0] = MINVALUE;
 return H;
}
ci

void InsertHeap(PriorityQueue H, ElemType key)   //上濾過程
{
 if(H->Size == H->Capasity)
  throw exception("The priority queue is full!");
 int i;
 for(i = ++H->Size; H->elem[i / 2] > key; i /= 2)
 {
  H->elem[i] = H->elem[i / 2];
 }
 H->elem[i] = key;
}

int DeleteMin(PriorityQueue H)  //下濾過程
{
 int i, child;
 ElemType MinElem, LastElem;
 if(H->Size == 0)
 {
  cout << "The priority queue is empty!" << endl;
  return H->elem[0];
 }
 MinElem = H->elem[1];   //此時根節點能夠看作是一個空穴
 LastElem = H->elem[H->Size--];
 for(i = 1; i * 2 <= H->Size; i = child)   //比較空穴i的兩個孩子,child指向最小的孩子
 {
  child = i * 2;
  if(child != H->Size && H->elem[child] > H->elem[child + 1])
   child++;
  if(LastElem > H->elem[child])  //最小的孩子與最後一個元素比較,若最後一個元素小於該節點的最小孩子,

                                                    //則將最後的元素填入空穴中
   H->elem[i] = H->elem[child];
  else
   break;
 }
 H->elem[i] = LastElem;  //填補空穴
 return MinElem;
}

int SearchKElem(ElemType arr[], int length, int k)
{
 if(length < k || length <MINHEAPSIZE)
  throw exception("Invalidate input!");
 PriorityQueue H;
 H = InitializeHeap(k);
 for(int i = 0; i < k; i++)
  InsertHeap(H, arr[i]);
 for(int j = k; j < length; j++)
 {
  if(arr[j] > H->elem[1])
  {
   DeleteMin(H);
   InsertHeap(H, arr[j]);
  }
 }
 ElemType kMax = H->elem[1];
 return kMax;
}

int main()
{
 int arr[10] = {5, 2, 8, 1, 3, 9, 7, 4, 6,10};
 int k;
 cout << "請輸入k值:" ;
 cin >> k;
 int result = SearchKElem(arr, 10, k);
 cout <<"倒數第k大的元素爲:" << result << endl;

 system("pause"); return 0;}

相關文章
相關標籤/搜索