使用堆尋找前K個最大值兼談程式最佳化(上)

來源:互聯網
上載者:User

     一、 緣起

        看到CSDN社區有篇《3秒搞定一億資料的前K個最大值》, 想想堆排序不是可以用來做這事嗎,於是就動手做做,看看堆排序能夠達到怎樣的效率。堆的原理就不多說了,網上有好多, 如果想參閱比較權威性的材料,可參見《演算法導論》第六章堆排序。 本文的程式即是建立其上。

     

     二、 程式

             KthMax.c

/*  * 使用堆排序求解前K個最大值問題 * */   #include "HeapUtil.c" /* 計算 list 中前 k 個最大值 */Elem* findkthMax(Elem *list, int k, long num);        void testkthMax(long NUM, int K);void measure(long NUM , int K);void testValid();void testPerf();int main(){      srand(time(NULL));    //test(" Test Valid ", testValid);  // test(" Measure Performace ", testPerf);    measure(100000000, 100);    getchar();  return 0;}void test(char *msg, void (*test)()){     printf("\n----------- %s ----------\n", msg);     (*test)();     printf("\n\n");}void testValid(){     long num;     int k;     for (num = 0; num <= 8; num += 1) {        for (k = 0; k <= num; k+=1) {           testkthMax(num, k);        }      }}/* 測試找出前K個最大值 */void testkthMax(long NUM, int K){      Elem *kthmax;      Elem* list = myalloc(NUM+1);      creatList(list, NUM);      printf("\nThe original list:\n");      printList(list, NUM);  kthmax = findkthMax(list, K, NUM);      printListInternal(kthmax, 0, K, K);        free(kthmax);      free(list);  printf("\n"); }void testPerf(){     long num ;      int k;     for (num = 1; num <= 100000000; num*=10) {        for (k = 1; k <= 1000 && k <= num ; k*=10) {           measure(num, k);        }      }}void measure(long NUM , int K){      clock_t start, end;      Elem *kthmax;      Elem* list = myalloc(NUM+1);      creatList(list, NUM);        start = clock();  kthmax = findkthMax(list, K, NUM);  end = clock();        free(kthmax);      free(list);       printf("\nNUM = %-10ld, K = %-10d, Eclipsed time: %6.3f\n", NUM, K, ((double)(end-start))/CLOCKS_PER_SEC);}/* 計算 list 中前 k 個最大值 */Elem* findkthMax(Elem *list, int k, long n){     long i;     long heapsize = n;     Elem *kthmax = myalloc(k);     Elem *p = kthmax; buildInitMaxHeap(list, n); for (i = n; i > n-k; i--) {           (p++)->num = list[1].num;        swap(&list[1], &list[i]);    heapsize--;    maxHeapify(list, 1, heapsize); } return kthmax;}

HeapUtil.c

/* HeapUtil.c * 實現建堆過程的函數  * * NOTE: 數組 list 的 0 號單位未用,元素應放置於[1:num]而不是[0:num-1] *       num 是應用中的資料數目, 因此必須給數組分配至少 num+1 個元素空間, */#include "common.c"#define LEFT(i)     (2*(i))#define RIGHT(i)    (2*(i)+1)/*  * maxHeapify: 使以結點i為根的子樹為最大堆.  * 前置條件:結點i的左右子樹都滿足最大堆性質  */void maxHeapify(Elem list[], long i, long heapsize);  void buildInitMaxHeap(Elem list[], long num);    /* BuildMaxHeap: 構造初始最大堆 */void swap(Elem *e1, Elem *e2);void maxHeapify(Elem list[], long i, long heapsize){  long largest = i;     /* 結點i與其左右孩子節點中關鍵詞最大的那個結點的下標 */  long lch = LEFT(i);  long rch = RIGHT(i);    if (lch <= heapsize && list[lch].num > list[largest].num) {    largest = lch;      }      if (rch <= heapsize && list[rch].num > list[largest].num) {   largest = rch;      }    if (largest != i) {   swap(&list[largest], &list[i]);       maxHeapify(list, largest, heapsize);      }}/*  * buildMaxHeap: 構造初始最大堆  */void buildInitMaxHeap(Elem list[], long num){ long i; for (i = (num+1)/2; i >= 1; i--)    maxHeapify(list, i, num);}void swap(Elem *e1, Elem *e2){  Elem e;  e = *e1;  *e1 = *e2;  *e2 = e;} 

common.c

/*  * common.c 存放公用結構與常式  */ #include <stdio.h>#include <stdlib.h>#include <time.h>#include <limits.h>  typedef struct {  long  num;   /* 設為關鍵詞 */ } Elem;void creatListInternal(Elem *list, long offset, long num, long len);void printListInternal(Elem *list, long offset, long num, long len);void creatList(Elem *list, long num);void printList(Elem *list, long num);Elem *myalloc(long size);long randNum();/*  * 隨機產生列表元素,並存入從offset開始的 num 個元素, len 是為列表長度  * 若 len < offset + num 則只存入 len-offset個元素  */void creatListInternal(Elem *list, long offset, long num, long len){   long i, endIndex = offset+num;   srand(time(NULL));   if (offset < 0 || offset >= len) {            return ;       }   if (endIndex > len) {          endIndex = len ;       }   for (i = offset; i < endIndex; i++)      (*(list+i)).num = randNum();}/*  *  列印從offset開始的num個元素, len 是為列表長度 *  若 len < offset + num 則只列印 len-offset個元素  */ void printListInternal(Elem *list, long offset, long num, long len){   long i, endIndex = offset+num;   if (offset < 0 || offset >= len) {            return ;       }   if (endIndex > len) {          endIndex = len ;       }   for (i = offset; i < endIndex; i++)       printf("%3d%c", (*(list+i)).num, ((i-offset+1)%10 == 0) ? '\n': ' ');       printf("\n");}void creatList(Elem *list, long num){     creatListInternal(list, 1, num, num+1);}void printList(Elem *list, long num){     printListInternal(list, 1, num, num+1);}Elem *myalloc(long size){     Elem* list = (Elem *)malloc(size*sizeof(Elem));     if (!list) {          fprintf(stderr, "fail to allocate memory.");          exit(1);      }      return list;}long randNum(){      return ((1 + rand()) % INT_MAX) * ((1 + rand()) % INT_MAX);}

HeapSort.c

/* * HeapSort.c 實現堆排序  */ #include "HeapUtil.c"#define NUM 10  void heapSort(Elem list[], long num);void testHeapSort();  int main(){    printf("*********** test heap sort ********");    testHeapSort();    getchar();    return 0;} void heapSort(Elem list[], long num){     long i, heapsize = num;     buildInitMaxHeap(list, num);          for (i = num; i >= 1; i--) {        swap(&list[1], &list[i]);        heapsize--;        maxHeapify(list, 1, heapsize);     }}   /* 測試堆排序 */void testHeapSort(){        Elem list[NUM+1];      creatList(list, NUM);  printf("\nThe original list:\n");  printList(list, NUM);  printf("\n");  heapSort(list, NUM);   printf("\nThe sorted list:\n");  printList(list, NUM);  printf("\n"); }  

 三、 時間測量

  

        四、 效率分析

        使用堆的方法在n個數值中尋找前K個最大值的時間效率為 O(n + k*log2n) ,其中建立初始最大堆(即buildInitMaxHeap)的時間為O(n) ,而每次尋找均需要約 O(log2n) , 合起來即是前述結果。 因此, 總時間是隨總元素數目線性增長的, 而當K遠小於 n 時, 尋找前K個最大值所耗費的時間差別不大。

        五、 程式最佳化的起點

        從結果中可以看到,比別人的整整慢一倍! 還有的能達到0.1s層級,當然,這可能與機器硬體設定有關,不過,無論如何,也阻擋不住我進行程式最佳化的決心了。之前學過一些最佳化的方法手段,正好在這程式上練練手,鞏固下。 請期待下文。

     

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.