• 跳跃回溯____寻找最长平台


    已知一个已经从小到大排序的数组,这个数组中的一个平台就是连续的一串值相同的元素。例如在1,2,2,3,3,3,4,5,5,61, 2.2, 3.3.3, 4, 5.5, 6都是平台。尝试编写一个程序,接受一个从小到大排好序的数组,把这个数组中最长的平台找出来。例如,在上面的例子中,3.3.3就是这个数组最长的平台。 

    以下内容转载至:http://hxraid.iteye.com/blog/655389 

    1、经典最长平台算法

     已知一个已经从小到大排序的数组,这个数组中的一个平台(Plateau)就是连续的一串值相同的元素 ,并且这一串元素不能再延伸。例如,在 1,2,2,3,3,3,4,5,5,6中[1]、[2,2]、[3,3,3]、[4]、[5,5]、[6]都是平台。是编写一个程序,接受一个数组,把这个数组中最长的平台找出来。在上面的例子中3,3,3就是该数组中最长的平台。
    【说明】
    这个程序十分简单,但是要编写好却不容易,因此在编写程序时应该考虑下面几点:
    (1) 使用的变量越少越好;
    (2) 把数组的元素每一个都只查一次就得到结果;
    (3) 程序语句也要越少越好。
    这个问题曾经困扰过David Gries 这位知名的计算机科学家。本题与解答取自David Gries 编写的有关程序设计的专著。

    #include  <stdio.h>
    int longest_plateau()
    {
         int  x[] = { 3, 4, 4, 7, 8, 9, 9, 9, 9, 10};
         int  n   = sizeof(x)/sizeof(int);
         int  length = 1;         /* plateau length >= 1.     */
         int  i;
    
         for (i = 1; i < n; i++)
              if (x[i] == x[i-length])
                   length++;
         return length;
    }
    这是一个时间复杂度为O(n) 的经典算法,其代码十分简练。

    另外,我自己也写了一个时间复杂度为O(n)的算法,原理就是找出所有平台分界位置,后一个位置减前一个位置(平台长度)的最大值。 

    int longest_plateau(){
        int  keys[] = {3, 4, 4, 7, 8, 9, 9, 9, 9, 10};
        int  size = sizeof(keys)/sizeof(int);
    
        int maxLength=0; //记录最大的平台长度
        int maxLowBound=-1; //记录最大平台的首偏移
        int maxHighBound=-1; //记录最大平台的尾偏移
    
        int index=1; // 两个分界点位置进行一次比较,index=0或1
        int bound[2]; //记录每一次比较长度的首,尾偏移
    
        bound[0]=1;//第一个平台的首偏移在数组第0个位置
        for(int i=1;i<size;i++){
    
            if(keys[i]!=keys[i-1]){
                bound[index++]=i; //当前平台的尾偏移(即下一个平台的首偏移)
            }
    
            if(index==2){ //比较平台长度
                if(bound[1]-bound[0]>maxLength){
                    maxLength=bound[1]-bound[0];
                    maxLowBound=bound[0];
                    maxHighBound=bound[1];
                }
                bound[0]=bound[1]; //当前平台的尾偏移成为下一个平台的首偏移
                index=1; //准备记录下一个平台的尾偏移到Bound[1]上
            }
        }
    
    //    printf("longest plat's length is %d\n",maxLength);
    //    printf("longest plat is ");
    //    for(int j=maxLowBound;j<maxHighBound;j++){
    //        printf("%d",keys[j]);
    //    }
    
        return maxLength;
    }

    2、改进的最长平台算法 

    上面O(n)的时间复杂度级别已经很不错了,但是如果n值特别大,那么仍然要比较n次才可以出结果,我们能不能降低比较次数呢?显然,这个问题是可以优化的。

    我们再来回顾一下David Gries的经典算法(代码1的line: 9),不管当前最长平台的长度为多少,每一次比较都是i++。难道每一次比较都是必须的吗? 比如下面这个平台串:

                                                  pArr[]:    1  1  1  2  2  2  2   2  3  3    4   4    5    5

                                                  index:     0  1  2  3  4  5  6  7  8   9  10  11  12  13

    分析: 当pArr[2]==pArr[0]的时候,最长平台长度已经增到了3。此时继续比较pArr[3]==pArr[0]发现不相等。那么说明pArr[3]已经开始了一个新的平台。依据经典算法,我们还要继续比较pArr[4]==pArr[1],pArr[5]==pArr[2]。显然,这两个比较是不必要的,因为pArr[3]开始了新的平台,位置3之前的所有数据都不会和3之后的所有数据相等了。

    根据上面的分析,我们很容易的想到可以跳跃一定的次数进行比较,跳跃多少呢?最简单的想法就是跳跃一个当前的longest(最长平台长度)。因为如果当前pArr[index]==pArr[index+longest]的话,说明当前平台长度比上一次的longest还要长,如果pArr[index]!=pArr[index+longest]的话,那么目前的平台长度绝对不会超过longest,也就没有必要再去比较小于longest的平台长度是多少了。

    问题并没有想象的那么简单,跳跃longest长度之后,为了下一次还能够跳跃longest长度,有的时候是需要回溯一段距离的。 我们来看看下面的详细算法分析。

    还是上面的例子,我们来一步一步的研究这个改进的算法。

    (1)  首先计算第一个平台 "1  1  1" 得到了当前最长平台长度为longest=3,当前串位置index=3。

    (2)  这时我们比较pArr[index]==pArr[index+longest](即比较pArr[3]<->pArr[6])。显然相等,那么longest++(即longest=4)。然后继续循环比较pArr[index]==pArr[index+longest],直到不相等为止。此时index=8, longest=5.

    (3)  这一步非常重要,当前的index=8已近开始了一个新的平台, 而当前的longest=5。继续比较pArr[index]==pArr[index+longest](即比较pArr[8]<->pArr[13]),发现不相等。此时我们能不能继续从pArr[13]开始向后跳跃longest=5的长度呢。显然不对,因为pArr[13]并不是平台5的开始位置,pArr[12]=5。如果跳跃longest长度,后面的计算结果将全部错误。 此时,我们必须从13开始回头遍历,直到找到平台的其实位置pArr[12],然后从12位置开始跳跃longest=5的长度才可以。

    //跳跃最长平台算法
    int jump_longest_plateau(int * pArr, int size){
        int longest=1; //最长平台长度
        int index=1;  //平台串位置索引
        //计算第一个平台的长度
        for(;index<size&&pArr[index]==pArr[index-1];index++,count++)
            ++longest;
        //跳跃longest比较平台长度
        while((index+longest)<size){
            // 如果跳跃之后相等,则循环继续增大最长平台的长度
            if(pArr[index]==pArr[index+longest]){
                while(pArr[index]==pArr[index+longest]){
                    ++longest;
                }
                index+=longest;
            }else{ //如果跳跃之后不相等,则回溯寻找当前平台的起始位置
                index+=longest;
                for(;pArr[index]==pArr[index-1];index--);
            }
        }
        return longest;
    } 

    算法分析:时间复杂度仍然是O(n)级别 (注意:不要看到双重循环就认为是O(n^2)级别)。随然有的时候需要回溯到平台的起始位置,但改进之后的算法仍然降低了比较次数。 因为跳跃longest后最多需要回溯longest-1次(此时共比较longest次)。也就是最差情况下位置index每次跳跃之后都会回溯到index+1的位置上,因此最差情况下改进算法会蜕化成经典算法的比较次数n。 

    我们列举出一个最差情况的平台串:  1  1  2  2  3  3  4  4  5  5  6  6  7  7 .... 

    平均而言,1000个长度的随机初始化平台串,改进算法的比较次数在149次左右。而经典算法必须比较1000次。 

    我的渣渣代码:
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <math.h>
    
    void fun(int array[],int arraySize)
    {
        int numMax, num, timesMax, times, i;
    
        for(i = 1, num = numMax = array[0], times = timesMax = 1; i < arraySize; i++)
        {
            if( array[i] == num )
                times++;
            else
            {
                if( times > timesMax)
                {
                    numMax = num;
                    timesMax = times;
                }
                times = 1;
                num = array[i];
            }
        }
        printf("timesMax:%d  numMax:%d\n", timesMax, numMax);
    }
    int main()
    {
        int a1[] = {1,2,2,3,3,3,4,5,5,6};
    
        fun(a1, sizeof(a1)/sizeof(a1[0]));    
        return 0;
    }
  • 相关阅读:
    你认为做好测试计划工作的关键是什么?
    一套完整的测试应该由哪些阶段组成?
    你对测试最大的兴趣在哪里?为什么?
    如何测试一个纸杯?
    黑盒测试和白盒测试各自的优缺点
    在您以往的工作中,一条软件缺陷(或者叫Bug)记录都包含了哪些内容?如何提交高质量的软件缺陷(Bug)记录?
    测试人员在软件开发过程中的任务
    软件测试分为几个阶段? 各阶段的测试策略和要求是什么?
    软件测试的策略
    软件产品质量特性
  • 原文地址:https://www.cnblogs.com/wwjyt/p/3153120.html
Copyright © 2020-2023  润新知