算法系列之常用算法之一----分治算法


一、基本概念

在計算機科學中,分治法是一種很重要的算法。分治算法,字面上的解釋是“分而治之”,分治算法主要是三點:

1.將一個復雜的問題分成兩個或更多的相同或相似的子問題,再把子問題分成更小的子問題----“分”

2.將最后子問題可以簡單的直接求解----“治”

3.將所有子問題的解合並起來就是原問題打得解----“合”

這三點是分治算法的主要特點,只要是符合這三個特點的問題都可以使用分治算法進行解決(注意用詞,是”用”,至於好不好就是另外一回事了)

二、分治法的特征

分治法所能解決的問題一般具有以下幾個特征:

1) 該問題的規模縮小到一定的程度就可以容易地解決

2) 該問題可以分解為若干個規模較小的相同問題,即該問題具有最優子結構性質。

3) 利用該問題分解出的子問題的解可以合並為該問題的解;

4) 該問題所分解出的各個子問題是相互獨立的,即子問題之間不包含公共的子子問題。

第一條特征是絕大多數問題都可以滿足的,因為問題的計算復雜性一般是隨着問題規模的增加而增加;

第二條特征是應用分治法的前提它也是大多數問題可以滿足的,此特征反映了遞歸思想的應用;、

第三條特征是關鍵,能否利用分治法完全取決於問題是否具有第三條特征,如果具備了第一條和第二條特征,而不具備第三條特征,則可以考慮用貪心法或動態規划法。

第四條特征涉及到分治法的效率,如果各子問題是不獨立的則分治法要做許多不必要的工作,重復地解公共的子問題,此時雖然可用分治法,但一般用動態規划法較好

三、為什么用分治法?怎么正確使用分治法?

為什么用分治算法?我們使用一種算法的原因大部分情況下都是為了”快“,只有在少數情況下,在程序已經足夠”快“的前提下,我們才會犧牲一部分的”快“,去保全一些開發因素(比如,程序的可維護性等等),那么分治算法為什么快?我們在用這個算法之前必需理解清楚這個問題。

分治算法的思想就是將一個問題規模比較大的問題划分為幾個相同邏輯性質(或者直接理解為類似)的問題規模變小的子問題。我們可以從這里入手。

舉個超級簡單的例子:

假如有一個存在n個元素的int型數組,我們需要求該數組的和。

可能有些人想不想就是一個分治算法,將這個問題分為兩個子問題,然后每個子問題再分為兩個子問題,當子問題的規模為只有兩個數時進行相加。。。

然而,這種辦法是使用了分治算法,可是效率比直接遍歷一遍相加得到的效率還要低的多.

為什么?因為分治算法本身不適合這種單次遍歷就可以搞定的簡單問題。你們在閱讀一遍分治算法的思想:分治算法的思想就是將一個問題規模比較大的問題划分為幾個相同邏輯性質的問題規模變小的子問題,那么這個定義存在一個隱含的前提,當問題規模比較大時,該問題解決起來要成倍的困難!

我們可以舉這樣一個簡單的例子:
我們對一個存在n個元素的數組,使用簡單排序進行排序時:

當n=1時,無需比較

當n=2時,我們需要1次比較

當n=3時,我們需要3次比較

當n=4時,我們需要6次比較

當n的數值比較大時,我們需要比較的次數越來越多將會是一個巨大的數字。

而對於前面的求和的例子:
當n=1時,無需相加

當n=2時,我們需要1次相加

當n=3時,我們需要2次相加

當n=4時,我們需要3次相加

仔細觀察這組數據,是否發現了什么?

對於求和的例子來說,該問題的計算量與問題規模成正比,在相同的條件下,我們根本無須使用分治算法,因為即使這個問題規模變大,他的解決問題的難易程度沒有絲毫改變,它所付出的,只不過是增大了問題規模后所必須付出的計算量,概括起來就是線性增長的問題規模導致了線性增長的計算量。

而對於排序的例子,當問題規模變大時,計算量的增大是成冪次型增長的,概括起來就是線性增長的問題規模導致了冪次型計算量的增長。使得問題規模大的問題解決起來更加困難。

綜合起來概括,在問題規模與計算量成正比的算法中,分治算法不是最好的解法,並且有可能是效率極其底下的算法。如果存在某個問題,線性增長的問題規模可能帶動計算量的非線性增長,並且符合分治算法的三個特征,那么分治算法是一個很不錯的選擇。

四、實例解析

1.二分查找

實例分析:也許有人看到這里認為二分查找算法與我前面的推論是矛盾的,但其實並不矛盾。不矛盾的原因在於二分查找算法是存在前提條件的。二分查找的數組必須是有序狀態的,二分查找是根據它的規則特性人們找到的一種取巧的方法。對於一個無序的查找方法,我的結論依然有效。

代碼來一波:

image

 

2.棋盤覆蓋

問題描述: 一個2^k×2^k 個方格組成的棋盤中,恰有一個方格與其他方格不同,稱該方格為一特殊方格,且稱該棋盤為一特殊棋盤。在棋盤覆蓋問題中,要用圖示的4種不同形態的L型骨牌覆蓋給定的特殊棋盤上除特殊方格以外的所有方格,且任何2個L型骨牌不得重疊覆蓋。

image

 

實例分析:每次都對分割后的四個小方塊進行判斷,判斷特殊方格是否在里面。這里的判斷的方法是每次先記錄下整個大方塊的左上角(top left coner)方格的行列坐標,然后再與特殊方格坐標進行比較,就可以知道特殊方格是否在該塊中。如果特殊方塊在里面,這直接遞歸下去求即可,如果不在,這根據分割的四個方塊的不同位置,把右下角、左下角、右上角或者左上角的方格標記為特殊方塊,然后繼續遞歸。在遞歸函數里,還要有一個變量s來記錄邊的方格數,每次對方塊進行划分時,邊的方格數都會減半,這個變量是為了方便判斷特殊方格的位置。其次還要有一個變nCount來記錄L型骨牌的數量。

代碼秀:

image

 

運行結果:

image

 

3.歸並排序/合並排序

實例分析:咳咳,大名鼎鼎的合並排序不用我多說吧。。。缺點是需要多占一些內存充當緩沖區。。

上代碼:

image

 

4.快速排序

運行結果:有了合並排序怎么可以沒有快速排序咧

代碼:

image


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM