在連續數字序列中快速找出連續數字之和等於指定值的子序列
這是我視訊面試時做過的一道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
, 上面的程式不能滿足要求。剩下的工作就交給大家,一起動動腦,動動手,活動活動。