1. 程式人生 > 其它 >有趣的演算法(十一) ——分治法:快速​求最值

有趣的演算法(十一) ——分治法:快速​求最值

有趣的演算法(十一)——分治法:快速求最值

(原創內容,轉載請註明來源,謝謝)

一、需求

一個數組,裡面有若干的數字,現需要得到這一組數字的最大值和最小值。

二、簡單分析

最基本的做法,是兩兩比對,可以區分出臨時的最大值和最小值,再拿臨時的最大值和最小值往後比較,有新的最值則更新。總的需要的比較次數是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