有趣的演算法(十一) ——分治法:快速求最值
阿新 • • 發佈:2022-05-03
有趣的演算法(十一)——分治法:快速求最值
(原創內容,轉載請註明來源,謝謝)
一、需求
一個數組,裡面有若干的數字,現需要得到這一組數字的最大值和最小值。
二、簡單分析
最基本的做法,是兩兩比對,可以區分出臨時的最大值和最小值,再拿臨時的最大值和最小值往後比較,有新的最值則更新。總的需要的比較次數是2n-2。
三、優化
使用分治法快速求最值。即把陣列分到最小的1-2個數,兩兩比較後,僅將最大值和最小值回傳,再兩兩比較最值,回傳新的最值,最終得出最大值和最小值。
分析需要比較的次數。當陣列只有1個數時,T(1)=0;2個數時,T(2)=1。n個數字時,T(n)=2T(n/2)+2。
推導T(n)=2T(n/2)+2= 22T(n/22)+22+2…=2k-1T(2)+2k-1+2k-2+…22+2=2k-1+2k-2。
因此,當n=2k時,需要的次數是n/2+n-2=3n/2-2。當n不是2k,則次數會比3n/2-2略多,正好2的k次的陣列長度時,這種演算法較快。
四、實現
使用php程式設計,程式碼如下:
<?php $x = 0; //快速求最值-返回 array(min, max) function quickMost(array $nums) { $len = count($nums); if(1 == $len) { returnarray(reset($nums), reset($nums)); } if(2 == $len) { $min =reset($nums); $max =next($nums); if($min >$max) { $tmp= $min; $min= $max; $max= $tmp; } $GLOBALS['x']++; returnarray($min, $max); } $arr1 =array_slice($nums, floor($len/2)); $arr2 =array_diff($nums, $arr1); list($min1, $max1) =quickMost($arr1); list($min2, $max2) =quickMost($arr2); $min = $min1 <=$min2 ? $min1 : $min2; $GLOBALS['x']++; $max = $max1 >=$max2 ? $max1 : $max2; $GLOBALS['x']++; return array($min,$max); } $testArr = array(7,8,5,1,4,9,0,3); $res = quickMost($testArr); var_dump($res); echo $x;
結果如下:
aarray(2) { [0]=> int(1) [1]=> int(9) }
10
正確算出了最值,數字個數為8,則預測的比較次數為3n/2-2=3*8/2-2=10,和輸出結果一致。
說明:
這裡用到裡一個php的array_diff,返回的是一個數組有的且另一個數組沒有的數字,這樣一定程度上如果有重複數字可以減少比較的次數。
但是,存在問題,當diff後,由於是返回一個差集,因此第二個陣列可能是空樹組的情況,例如輸入的需要比較的陣列為(1,1,1,1,1,1),此時的$arr2會是空樹組,則會報錯。因此,需要在第二次遞迴呼叫quickMost方法時,進行一個判斷,即可正確解決此問題。修改如下:
if(false == empty($arr2))
{
list($min2, $max2) =quickMost($arr2);
$min = $min <= $min2 ?$min : $min2;
$GLOBALS['x']++;
$max = $max >= $max2 ?$max : $max2;
$GLOBALS['x']++;
}
——written by linhxx 2018.01.18