zoukankan      html  css  js  c++  java
  • 使用堆查找前K个最大值兼谈程序优化(上)


         一、 缘起

            看到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级别,当然,这可能与机器硬件配置有关,不过,无论如何,也阻挡不住我进行程序优化的决心了。之前学过一些优化的方法手段,正好在这程序上练练手,巩固下。 请期待下文。



         

  • 相关阅读:
    重打技术征集系统第七稿开发第1、2天
    关于centos7右上角网络图标消失的解决办法
    记录一下hbase踩的坑
    在jsp中,单选按钮的点击事件(点击隐藏或显示输入框)
    input中禁止输入特殊字符
    《软件方法》阅读笔记——1
    随手快递app开发第十七天
    Codeforces Round #284 (Div. 1) B. Name That Tune(最大流)
    hihocoder 1310岛屿(dfs,一个搜索技巧)
    Codeforces Round #294 (Div. 2) E. A and B and Lecture Rooms(lca+思维,树上寻找与给定两个点距离相等的点的个数)
  • 原文地址:https://www.cnblogs.com/lovesqcc/p/4037820.html
Copyright © 2011-2022 走看看