zoukankan      html  css  js  c++  java
  • 【算法题】TopK相关问题简述

    最近几天,看了与topk相关的几个问题,在这里做一些记录。

    问题一:已知一个数组,求取数组的最小值和最大值。

    方法一:将求取最小值和最大值看成单独的问题,每个问题单独解决,具体实现如下:

     1 void MinMaxValue(int* data,int n,int& min_v,int max_v)
     2 {
     3     //求取最小值
     4     min_v = data[0];
     5     for(int i = 1;i < n;i++)
     6     {
     7         if(data[i] < min_v)
     8             min_v = data[i];
     9     }
    10     //求取最大值
    11     max_v = data[0];
    12     for(int i = 1;i < n;i++)
    13     {
    14         if(data[i] > max_v)
    15             max_v = data[i];
    16     }
    17 }

    方法二:维持两个变量min和max,每次取两个数,比较1次,较小的和min比较,决定是否更新min;较大的和max比较,决定是否更新max。具体实现如下:

     1 void MinMaxValue(int* data,int n,int& min_v,int max_v)
     2 {
     3     min_v = max_v = data[0];
     4     int min_tmp,max_tmp,i;
     5     for(i = 1;i < n - 1;i += 2)
     6     {
     7         if(data[i] < data[i + 1])
     8         {
     9             min_tmp = data[i];
    10             max_tmp = data[i + 1];
    11         }
    12         else
    13         {
    14             min_tmp = data[i + 1];
    15             max_tmp = data[i];
    16         }
    17         if(min_tmp < min_v)
    18             min_v = min_tmp;
    19         if(max_tmp > max_v)
    20             max_v = max_tmp;
    21     }
    22     //处理最后落单的数据
    23     if(i == n - 1)
    24     {
    25         if(data[i] < min_v)
    26             min_v = data[i];
    27         if(data[i] > max_v)
    28             max_v = data[i];
    29     }
    30 }

    正上述代码所示,每处理二个数据,需要比较3次,其总的比较次数为1.5n。

    方法三:将数组两两分组,并比较,将小数置于数组前半部分,大数置于数组所半部分;然后在数组的前半部分进行比较找到最小的数,在数组的后半部分找到最大的数;如果n为奇数,将数组中间数最小值和最大值比较,得到最终的最小值和最大值。具体实现如下:

     1 void MinMaxValue(int* data,int n,int& min_v,int& max_v)
     2 {
     3     int i = 0,j = n - 1;
     4     while(i < j)
     5     {
     6         if(data[i] > data[j])
     7         {
     8             //swap data[i],data[j]
     9             int tmp = data[i];
    10             data[i] = data[j];
    11             data[j] = data[i];
    12         }
    13         i++;
    14         j--;
    15     }
    16     min_v = data[0];
    17     for(i = 1;i < (n + 1)/2;i++)
    18     {
    19         if(data[i] < min_v)
    20         {
    21             min_v = data[i];
    22         }
    23     }
    24     max_v = data[(n + 1)/2];
    25     for(i = (n + 1) / 2 + 1;i < n;i++)
    26     {
    27         if(data[i] > max_v)
    28         {
    29             max_v = data[i];
    30         }
    31     }
    32     if(n % 2)
    33     {
    34         int mid = n / 2;
    35         if(data[mid] < min_v)
    36         {
    37             min_v = data[mid];
    38         }
    39         if(data[mid] > max_v)
    40         {
    41             max_v = data[mid];
    42         }
    43     }
    44 }

    方法四:分而治之,将数组分成两半,递归计算前一半的min和max;递归计算后一半的min和max;然后比较前一半min和后一半的min取二者最小值,比较前一半的max和后一半的max取二者最大值。具体实现如下:

     1 void MinMaxValue(int* data,int begin,int end,int& min_v,int& max_v)
     2 {
     3     if(end - begin == 1)
     4     {
     5         min_v = max_v = data[begin];
     6         return;
     7     }
     8     int min_v_front,min_v_back,max_v_front,max_v_back;
     9     MinMaxValue(data,begin,begin + (end - begin) / 2,min_v_front,max_v_front);
    10     MinMaxValue(data,begin + (end - begin) / 2,end,min_v_back,max_v_back);
    11     min_v = (min_v_front < min_v_back)? min_v_front : min_v_back;
    12     max_v = (max_v_front < max_v_back)? max_v_front : max_v_back;
    13 }

    问题二:已知一数组,求取数组的最大值和次大值。

    这个问题与第一个问题类似,其区别只是以次大值取代最小值,解决该问题的思路也可依据前面的四种解决。其所需的比较次数也是O(1.5n)。下面简单的实现:

     1 void MaxMax2Value(int* data,int n,int& max_v,int& max2_v)
     2 {
     3     if(data[0] < data[1])
     4     {
     5         max_v = data[1];
     6         max2_v = data[0];
     7     }
     8     else
     9     {
    10         max_v = data[0];
    11         max2_v = data[1];
    12     }
    13     for(int i = 2;i < n;i++)
    14     {
    15         if(data[i] > max2_v)
    16         {
    17             if(data[i] < max_v)
    18             {
    19                 max2_v = data[i];
    20             }
    21             else
    22             {
    23                 max2_v = max_v;
    24                 max_v = data[i];
    25             }
    26         }
    27     }
    28 }

    另一种方法,利用比较过程中的中间结果。

    思路如下:对n个数两两比较,每次比较的较大值进入下一轮;对最多ceil(n/2)个较大值继续上述两两比较,得出较大值进下一轮比较;最后得出的较大值即为最大值。共进行了n-1次的比较操作。其关键点是:第二大的数一定是与最大值进行过比较的那些数中间的最大值。由于最大值一共进行了ceil(logn)次比较,因此我们需要从这ceil(logn)个数中找最大值,比较次数为ceil(logn)-1。总的比较次数为n+ceil(logn) -2。

    该方法的比较次数比1.5n少,但是需要额外的存储空间。

    问题三:找出无序数组的第K大的数。

    针对该问题,若K=2时,则可以参用问题二中的解决方法。

    K!=2时,可以利用排序先对数组进行排序,然后输出data[k-1]即为第K大的数,但这种方法的时间复杂度为O(n*log(n)).

    另一种方法,利用STL中提供的nth_element()可以很方便的求出第K大的数,其平均的时间复杂度为O(n).

    nth_element()的算法原理是利用快速排序的思想,从数组data中随机找出一个元素X,把数组分为两部分Sa和Sb。Sa中的元素大于等于X,Sb中元素小于X。这时有两种情况:1)Sa元素的个数小于k,则Sb中的第k-|Sa|个元素即为第k大数;2)Sa中元素的个数大于等于K,则返回Sa中的第k大数。

    从该问题出发,利用nth_element()解决效率更好。但是在实际应用中,可能会根据具体应用情况而选用排序方法。

    假设对于数组data,我们需要找出第K大的数,k={2,5,10,...},相当于对同一数组有m个求第K大数的请求。

    若此时,利用nth_element()解决时,其时间复杂度为O(n*m),而利用排序的方法,则只需排序一次,以后每次请求第K大数都是线性的,其时间复杂度为O(n*log(n)).由此可见,具体应用中选用什么样的算法,需要根据应用需求确定。

    参考资料:

    http://hi.baidu.com/silverxinger/item/92d3d6c11b511b5dad00efa1

    http://blog.jobbole.com/25710/

  • 相关阅读:
    XP系统下快速切换ip的bat脚本配置
    Spring学习札记
    hibernate防止sql注入
    重载,继承,重写和多态的区别:
    Oracle Sql基础
    Android开发——利用Cursor+CursorAdapter实现界面实时更新
    Android开发——09Google I/O之让Android UI性能更高效(1)
    Android开发——MediaProvider源码分析(2)
    Android开发——Android搜索框架(二)
    [转]activity的启动方式(launch mode)
  • 原文地址:https://www.cnblogs.com/dwdxdy/p/2654329.html
Copyright © 2011-2022 走看看