在连续数字序列中快速找出连续数字之和等于指定值的子序列

这是我视频面试时做过的一道PHP题目,题目其实并不复杂,是一道常见笔试题的变种。自己年纪大了,好久没做过笔试题,有点生疏。当时反应变慢了,写了个愚蠢的答案,这里总结一下,活动活动自己的脑子,同时给出自己更好的答案,大家可以一起来讨论,发表自己的看法。总结总是好的,因为它可以提高自己以后解决问题的能力和反应速度。题目要求如下:

给定一数字数列,如: 3,2,1,5,4,3,7,9, 返回 数字之和 等于指定值的 连续子序列,比如 要求数字之和 等于 12 返回连续子序列 2 1 5 4

说实话,自己刚开始有点愣,不过很快我给出了下面的答案,为了更清晰,方便讨论,我保持程序逻辑不变,改动了函数变量名,因为当时自己面试的时候有点乱。

 /**
  * @todo 返回数字之和等于指定值的连续子序列
  * @param array $array 数字数组
  * @param integer $targetSum 目标和
  * @return array
  */
 function getSubSequenceEqualTargetSum($array,$targetSum){
    $count = count($array);
    $sequence= [];
    for($i=0; $i<$count;$i++){
        $sequence= getSubSequence($array,$i,$count,$targetSum);
        if(count($sequence)>0){
            break;
        }
    }
    return $sequence;
}

/**
 * @todo 从数组任一下标开始查找数字之和等于指定值的连续子序列
 * @param $array 数组序列
 * @param $index 数组任一下标
 * @param $count 数组最大下标
 * @param $targetSum 目标和
 * @return array
 */
function getSubSequence($array,$index,$count,$targetSum){
    if($index<$count){
        $sum = 0;
        $sub = [];
        for($i=$index; $i<$count;$i++){
           $sum += $array[$i];
           array_push($sub,$array[$i]);
           if($sum == $targetSum){
             return $sub;
           }else if($sum > $targetSum){
             return [];
           }
        }
    }
    return [];
}

上面给出的答案,本身是可以正确返回指定序列的。思路是循环遍历数字序列,累加数字,直到刚好等于目标和,就直接返回;如果中途大于目标和,说明到目前为止这个连续子序列不合要求,我们从下一个下标开始重新累加。 不过第二个函数会频繁调用array_push, 会影响性能。所以我面试完又细想了一下,发现在子序列不合要求的情况下,从下一个下标开始重新累加数字之和的时候,不用重新计算,只需减去前面子序列开始位置的数字,然后再加上下一位数字即可。为此利用了一个临时变量作为指针,记录当前累加的子序列的开始位置。所以我给出的性能更好的答案如下:

 /**
  * @todo 返回数字之和等于指定值的连续子序列[快速版]
  * @param array $array 数字数组
  * @param integer $targetSum 目标和
  * @return array
  */
function getSubSequenceEqualTargetSumFast($array,$targetSum){
    $sum = 0;
    $start = 0;//指向子序列开始的位置
    $count = count($array);
    $sequence = [];
    for($i=0;$i<$count;$i++){//$i指向子序列最后的位置
        $sum += $array[$i];
        if($sum == $targetSum){
            $sequence = array_slice($array,$start,$i-$start+1);
        }else if($sum > $targetSum){
            $sum -= $array[$start];
            $start ++;
            //或者下面这种更简洁些的写法
            // $sum -= $array[$start++];
        }
    }
    return $sequence;
}

为了对比两个程序的性能,我故意构造了一个巨大的数组,会引起array_push的频繁调用,测试在糟糕的情况下,程序性能能提升到什么程度。

ini_set('memory_limit','512M');``` $arr = []; for($i=0;$i<1000000;$i++){ array_push($arr,1); } for($i=0;$i<1000000;$i++){ array_push($arr,-1); } for($i=0;$i<2000000;$i++){ array_push($arr,1); } $start = microtime(true); $sub = getSubSequenceEqualTargetSum($arr,2000000); $end = microtime(true); echo "<br/>用时 ".($end-$start); $start = microtime(true); $sub = getSubSequenceEqualTargetSumFast($arr,2000000); $end = microtime(true); echo "<br/>用时 ".($end-$start); //下面是测试结果 用时 0.75404405593872 用时 0.28001594543457

对比发现,程序提高了差不多2倍多。让我有点小意外,我以为会提高10倍以上。不过优化还是值得的。

举一反三
上面的答案也有一个不好的地方,就是只返回目标和的一个子序列,实际满足情况的可能会有多个子序列, 如文章开头等于12的子序列还有 5,4,3 , 还有一种比较特殊的情况,就是序列中间有 连续多个0 的情况,如 3 2 1 0 0 0 0 2 3, 上面的程序不能满足要求。剩下的工作就交给大家,一起动动脑,动动手,活动活动。

猜你喜欢

转载自blog.csdn.net/shb8845369/article/details/80782316