我們在實際應用中,對一個問題會有不同的解題思路,比如我們在讀書時候,往往對一道數學題目會有多種解題方法,可能有些方法比較簡單,有些方法比較復雜,步驟較多。所以找到一個合適的方法可以更快更好的去解決問題。在程序應用中,我們也會有不同的算法去解決問題。
算法分類分為:
1.基礎算法:包括字符串,數組,正則表達式,排序,遞歸等。
2.數據結構:堆,棧,隊列,鏈表,矩陣,二叉樹等。
3.高級算法:貪心算法,動態規划等。
根據問題的不同,一般可以有以下算法思想去解決問題:
遞推算法:
遞推法,就是從已知的結果和條件出發,利用特定關系分解中間步驟得出推論,逐步推導而得到結果。遞推算法分為順推和逆推兩種。
分治算法:
分治,顧名思義,分而治之,分治算法是一種化繁為簡的算法思想,往往應用於計算步驟比較復雜的問題,通過將問題簡化而逐步得到結果。常用場景就是求出最輕、最重問題(在一堆形狀相同的物品中找出最重或最輕的那一個,二分查找,快速排序和歸並排序,分治算法也是許多高效算法的基礎,比如快速傅立葉變換算法和 Karatsuba 乘法算法。
概率算法:
概率算法是在程序執行過程中利用概率統計的思路隨機地選擇下一個計算步驟,在很多情況下,算法在執行過程中面臨選擇時,隨機性選擇比最優選擇省時,因此概率算法可以在很大程度上降低算法的復雜度。
概率算法大致分類如下:
1.貝葉斯分類算法。
2.蒙特卡羅(Monte Carlo)算法。
3.拉斯維加斯(Las Vegas)算法。
4.舍伍德(Sherwood)算法。
5.隨機數算法。
6.近似算法。
7.機器學習算法中的的一些概率方法。
遞歸算法:
遞歸算法是指一種通過重復將問題分解為同類的子問題而解決問題的方法。具體來說就是就是一個函數或者類方法直接或間接調用自身的一種方法。
遞歸的優點:
1.實現簡單,代碼可讀性好。
2.在樹的前序,中序,后序遍歷算法中,遞歸的實現明顯要比循環簡單方便。
遞歸的缺點:
1.當需要處理的數據規模比較大的時候,遞歸算法入棧、出棧較多,每一次函數調用會在內存棧中分配空間,系統效率會很低,遞歸深度太深的話容易發生棧溢出。
2.容易重復計算。
-
遞歸算法設計三要素:
- 拆解問題,一個大問題可以被拆分等價於小問題的循環重復
- 遞歸返回階段,上一次自調用的結果是下一次調用的初始值。
- 邊界條件,就是明確遞歸什么時候結束,終止條件是啥,不能無限制地調用本身,必須有個出口。
遞歸算法的應用:
1.漢諾塔問題。
2.排列組合。
3.歸並排序。
4.斐波那契數列(Fibonacci)數列。
5.二分法遞歸查找。
遞歸算法的優化:
1.減少重復計算。
2.尾遞歸。
遞歸與循環:
遞歸與循環都是可以處理重復任務的問題,循環沒有函數調用開銷,但有時使用循環的算法並不會那么清晰地描述解決問題步驟。而遞歸常常會帶來性能問題,特別是在求解規模不確定的情況下。
下面看用遞歸來實現斐波那契數列
$stratTime = microtime(true); $startMemory = memory_get_usage(); function _fbnq($n){ if($n <= 0){ return 0; } if($n == 1 || $n == 2){ return 1; } return _fbnq($n - 1) + _fbnq($n - 2); } $arr= []; for ($i=1;$i<=26;$i++){ array_push($arr,_fbnq($i)); } echo implode($arr,',').PHP_EOL; $endTime = microtime(true); $runtime = ($endTime - $stratTime) * 1000; //將時間轉換為毫秒 $endMemory = memory_get_usage(); $usedMemory = ($endMemory - $startMemory) / 1024; echo "運行時間: {$runtime} 毫秒".PHP_EOL; echo "耗費內存: {$usedMemory} K".PHP_EOL;
執行結果
[root@oa-dev bin]# php index.php 1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368,75025,121393 運行時間: 1584.3920707703 毫秒 耗費內存: 1.359375 K
上面函數也可以優化成尾遞歸,要提供兩個累積變量。減少調用棧(call stack)。同時編寫遞歸代碼適合要先考慮退出遞歸的條件。
優化寫法:
$stratTime = microtime(true); $startMemory = memory_get_usage(); function fibonacci($n, $n1, $n2) { if($n <= 0) { return 0; } if($n <= 1) { return $n2; } return fibonacci($n - 1, $n2, $n1 + $n2); } $arr= []; for ($i=1;$i<=26;$i++){ array_push($arr,fibonacci($i,0,1)); } echo implode($arr,',').PHP_EOL; $endTime = microtime(true); $runtime = ($endTime - $stratTime) * 1000; //將時間轉換為毫秒 $endMemory = memory_get_usage(); $usedMemory = ($endMemory - $startMemory) / 1024; echo "運行時間: {$runtime} 毫秒".PHP_EOL; echo "耗費內存: {$usedMemory} K".PHP_EOL;
執行結果
[root@oa-dev bin]# php index.php 1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368,75025,121393 運行時間: 0.53906440734863 毫秒 耗費內存: 1.359375 K
窮舉算法:
窮舉也叫暴力破解法,在數學上也把窮舉法稱為枚舉法,就是在一個由有限個元素構成的集合中,把所有元素一一枚舉對比研究的方法。比如要找一個集合中最大的數,就把這個集合中的所有數都枚舉一遍,通過相互比較找出最大的那個數。
使用窮舉法解決問題,基本上就是以下兩個步驟:
- 確定問題的解(或狀態)的定義、解空間的范圍以及正確解的判定條件;
- 根據解空間的特點來選擇搜索策略,逐個檢驗解空間中的候選解是否正確;