Historically Highest Index algorithm HH-index算法

    算法课的ppt上看到一道习题,描述是这样的:

       In stock market, HH-index( historically highest ) of the current price ismeans that current price it the highest price in the previousk days, but not the highest one in the previousk+1 days. Given the price of n days, please give an algorithm ofO(n) time complexity to calculate the HH-index of all days.

       简要地说,就是求数组每个元素在以它为起始向前能作为最大值的区间。初看这道题很容易,O(n2)的方法是不言自明的,而且似乎有很大提升空间。想出这个O(n)的算法也只需要一点小机巧,诡异的是我不知道怎么归类这个问题。

        具体思路是,假设给定的数组是price[n],那么如果price[n] < price[n-1],HH[n] = 1;如果price[n] =  price[n-1],HH[n] = HH[n-1] +1;如果price[n] > price[n-1],那么price[n]必然是HH[n-1]天内的最大值,但是不确定再之前的值,所以,只需要继续比较price[n]和price[n-HH[n-1]]的值就可以了(这一步比较关键!),然后可以重复这个过程。求HH[n]的过程用伪码来表示就是

 

HH-CALCULATE

for ←1 to n

do   HH[i] ←1

if price[i] =price[i-1]

       then   HH[i] ←HH[i-1]

else if     price[i] > price[i-1]

       then    i-1

while     j>0 andprice[i]>=price[j])

         do  HH[i]←HH[i] +HH[j]

                  ← j-HH[j]

 

       复杂度分析:我只做了简单的形象化分析。把一个数组看成起起伏伏的波峰和波谷,于是这个数组就是由上坡过程和下坡过程组成的。那么显然在下坡过程中的点的比较次数很明显,都只有一次,总次数也就是下坡的长度。在上坡过程中,如图所示,圆点表示比较次数。从7到11的这个紫色上坡过程先是越过了6的波峰,比较次数为3,然后从11开始的黄色上坡过程就不会再与6的值比较。整个上坡过程的比较次数比上坡长度多了1,是由于10这个点翻越6时多比较了一次。归纳起来上坡过程的比较次数加起来也不会超过上坡的长度两倍(最多每个上坡都要翻越一次前面的波峰)。综上,总的比较次数必然少于2N。


        说这个问题不知道怎么归类是它这类似于一个DP问题(或者根本就是),某个问题(HH[n])取决于一系列子问题(HH[i], i<n),然而需要用到哪些子问题却是不确定的,于是不太好用数学描述,这和其他DP问题有些不太一样。这个问题解决了,似乎求HL(historically lowest)也是顺理成章,只要将函数稍稍改下就可以了。这时候,我发现一道雅虎的笔试题真和这个问题很像,可惜当时没有思考过这道题,于是脑残地写了个O(n2)的算法,然后……就没然后了。

       这道题也是一个数组,还画成了一个柱状图,为了方便叙述,假设柱状图被染色了,要求能求出一个面积最大的有颜色的矩形。如图所示。


       容易想到的是检查以每个数组元素为高的最大矩形。这个矩形的宽度应该向左右尽量延伸,如第7个元素向左延伸2个单位,向右延伸1个单位。于是能得到n个矩形(如果有些元素高相等,就可能有重复)。符合要求的矩形就是这n个矩形中面积最大的。

       对于某个元素而言,这个矩形向左延伸的宽度等于HL,向右延伸的宽度,姑且定义为FL(futurely lowerst)吧,它和HL是一个几乎一样的问题:如果把这个数组颠倒,那么HL就成了FL,FL就成了HL。

        FL和HL都是一遍扫描就可以搞定的,甚至可以一遍同时搞定。面积比较也是再扫一遍的事情,于是结果就是O(2n)了。写了这几个函数的代码,没什么好说的了……(哎,我真搓!)

[cpp]  view plain copy
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. void printArray(int* , int);  
  5. void getHH(int* , intint*);  
  6. void getHL(int* , intint*);  
  7. void getRect(int* , intint*, int*);  
  8.   
  9. int main(){  
  10. //  const int length = 10;  
  11.     int array[] = {1, 5, 3, 2, 8,  
  12.                      4, 3, 4, 1, 9};  
  13.     int length = sizeof(array)/sizeof(int);  
  14.     printArray(array, length);  
  15.     int HH[10], HL[10], FL[10];  
  16.     getHH(array, length, HH);  
  17.     getRect(array, length, HL, FL);  
  18.     return 1;  
  19.   
  20. }  
  21.   
  22.   
  23. void printArray(int* array, int length){  
  24.     int max = 0;  
  25.     for(int i = 0; i < length; i++){  
  26.         max = (array[i] > max)?array[i]:max;  
  27.     }  
  28.     for(int i = max; i > 0; i--){  
  29.         for(int j = 0; j < length; j++){  
  30.             if(array[j] >= i){  
  31.                 cout<<"__  ";  
  32.             }else{  
  33.                 cout<<"    ";  
  34.             }  
  35.         }  
  36.         cout<<endl;  
  37.     }  
  38.   
  39. }  
  40.   
  41. void getHH(int* array, int length, int* HH){  
  42.     cout<<"i =     ";  
  43.     for(int i  = 0; i <  length; i++){  
  44.         cout<<i<<"  ";  
  45.     }  
  46.     cout<<"\nHH[i] = ";  
  47.     for(int i  = 0; i <  length; i++){  
  48.         HH[i]  = 1;  
  49.         while(i>=HH[i]&&array[i]>=array[i-HH[i]])  
  50.             HH[i] += HH[i-HH[i]];  
  51.         cout<<HH[i]<<"  ";  
  52.     }  
  53.     cout<<endl;  
  54. }  
  55.   
  56. void getHL(int* array, int length, int* HL){  
  57.     int S = 0;  
  58.     int pre, last;  
  59.     for(int i  = 0; i <  length; i++){  
  60.         HL[i]  = 1;  
  61.         while(i>=HL[i]&&(array[i]<=array[i-HL[i]]))  
  62.             HL[i] += HL[i-HL[i]];  
  63.         if(array[i]*HL[i]>S){  
  64.             S = array[i]*HL[i];  
  65.             last = i;  
  66.             pre = last - HL[i];  
  67.         }  
  68.         //cout<<" i = "<<i<<" HL = "<<HL[i]<<endl;  
  69.     }  
  70. }  
  71.   
  72. void getRect(int* array, int length, int* HL, int* FL){  
  73.     int S = 0;  
  74.     int start, end;  
  75.       
  76.     for(int i  = 0; i <  length; i++){  
  77.         //scan the array to get HL and FL  
  78.         int i_rev = length-1-i;  
  79.         HL[i]  = FL[i_rev] = 1;  
  80.           
  81.         while(i>=HL[i]&&(array[i]<=array[i-HL[i]]))  
  82.             HL[i] += HL[i-HL[i]];  
  83.         while(i>=FL[i_rev]&& (array[i_rev] <= array[i_rev + FL[i_rev]]))  
  84.             FL[i_rev] += FL[i_rev + FL[i_rev]];  
  85.     }  
  86.       
  87.     for(int i = 0; i < length; i++){  
  88.         //find the rectangle  
  89.         cout<<"i = "<<i<<" HL = "<<HL[i]<<" FL = "<<FL[i]<<endl;  
  90.         if(array[i]*(HL[i]+FL[i]-1)>S){  
  91.             S = array[i]*(HL[i]+FL[i]-1);  
  92.             end = i + FL[i] -1;  
  93.             start =  i - HL[i] +1;  
  94.         }  
  95.     }  
  96.       
  97.     cout<<"Rectangle's area is "<<S<<" and starts at "<<start<<" ends at "<<end<<endl;  
  98.     return;  
  99. }  

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值