天天看點

算法之分治算法

分治算法( divide and conquer )的核心思想其實就是四個字,分而治之 ,也就是将原問題劃分成 n 個規模較小,并且結構與原問題相似的子問題,遞歸地解決這些

子問題,然後再合并其結果,就得到原問題的解。

這個定義看起來有點類似遞歸的定義。關于分治和遞歸的差別,我們在排序(下)的時候講過,分治算法是一種處理問題的思想,遞歸是一種程式設計技巧。實際

上,分治算法一般都比較适合用遞歸來實作。分治算法的遞歸實作中,每一層遞歸都會涉及這樣三個操作:

分解:将原問題分解成一系列子問題;

解決:遞歸地求解各個子問題,若子問題足夠小,則直接求解;

合并:将子問題的結果合并成原問題。

分治算法能解決的問題,

一般需要滿足下面這幾個條件:

原問題與分解成的小問題具有相同的模式;

原問題分解成的子問題可以獨立求解,子問題之間沒有相關性,這一點是分治算法跟動态規劃的明顯差別,等我們講到動态規劃的時候,會詳細對比這兩種

算法;

具有分解終止條件,也就是說,當問題足夠小時,可以直接求解;

可以将子問題合并成原問題,而這個合并操作的複雜度不能太高,否則就起不到減小算法總體複雜度的效果了。

分治算法可以求解的一些經典問題

1二分搜尋 2大整數乘法 3 棋盤覆寫 4 合并排序 5 快速排序 6線性時間選擇 7 最接近點對問題 8 循環賽日程表 9 漢諾塔

分治(Divide-and-Conquer§)算法設計模式如下

算法之分治算法

分治算法的基本步驟

  • 1 分治法在每一層遞歸上都有三個步驟:
  • 2 分解:将原問題分解為若幹個規模較小,互相獨立,與原問題形式相同的子問題
  • 3 解決:若子問題規模較小而容易被解決則直接解,否則遞歸地解各個子問題
  • 4 合并:将各個子問題的解合并為原問題的解。
算法之分治算法

使用分治算法解決漢諾塔問題

漢諾塔問題

漢諾塔:漢諾塔(又稱河内塔)問題是源于印度一個古老傳說的益智玩具。大梵天創造世界的時候做了三根金

剛石柱子,在一根柱子上從下往上按照大小順序摞着 64 片黃金圓盤。大梵天指令婆羅門把圓盤從下面開始按大小

順序重新擺放在另一根柱子上。并且規定,在小圓盤上不能放大圓盤,在三根柱子之間一次隻能移動一個圓盤。

算法之分治算法

分析:

A 如果是有一個盤, A->C

B 如果我們有 n >= 2 情況,我們總是可以看做是兩個盤 1.最下邊的盤 2. 上面的盤

  • 1 先把 最上面的盤 A->B
  • 2 把最下邊的盤 A->C
  • 3 把B塔的所有盤 從 B->C

可以看出 漢諾塔問題可以拆分成以上AB兩個大步,分解成的子問題可以獨立求解,子問題之間沒有相關性

如果有多個盤則分成AB 步驟,對AB步驟進行求解

設n個盤子的移動次數為T(n)

T(n)=2T(n-1)+1

T(1)=1

是以Hanoi塔算法的時間複雜度為O(2^n)

public class DivideAndConquer {

    public static void main(String[] args) {

        hanoiProblem(3, 'A', 'B', 'C');

    }


    /* 
     * @param: [discNum, discA, discB, discC] 
     * @return: void
     * @author: Jerssy
     * @dateTime: 2021/5/3 16:47
     * @description:
     */
    public static void   hanoiProblem(int discNum,char discA,char discB,char discC){

        if (discNum == 1) {

            System.out.println("the 1 disc move from "+discA+"-->"+discC);
        }
        else {

            hanoiProblem(discNum-1, discA, discC, discB);

            System.out.println("the "+discNum+" disc move from "+discA+"-->"+discC);

            hanoiProblem(discNum-1, discB, discA, discC);

        }

    }

}