分治算法

分冶算法的基本思想是將原問題分解為幾個規(guī)模較小的但類似原問題的子問題帐萎,遞歸地求解這些了問題比伏,然后再合并這些子問題的解來建立原問題的解

分冶算法在每層遞歸時都有三個步驟:

  • 分解,原問題分為若干子問題疆导,這些子問題都是原問題的規(guī)模較小的實例
  • 解決赁项,遞歸解決這些子問題,如果子問題的規(guī)模足夠小,則直接求解
  • 合并悠菜, 合并子問題的解成原問題的解

分治算法中需要使用遞歸舰攒,在使用遞歸時,一定要確定問題邊界悔醋,即問題規(guī)模較小的條件摩窃。常見的歸并排序以及求解數(shù)組中連續(xù)子數(shù)組和的最大值,都可以使用分治法解決

歸并排序

歸并排序的基本思想是芬骄,將數(shù)組分成兩個子數(shù)組猾愿,使用遞歸對兩個子數(shù)組進行排序,合并兩個已正確排序的子數(shù)組账阻。

注意蒂秘,遞歸的邊界條件即是,子數(shù)組中只有一個元素淘太。假定數(shù)組中只有兩個元素姻僧,分解成兩個子數(shù)組,每個子數(shù)組中只有一個元素琴儿,子數(shù)組中的元素此時當然就是已經(jīng)“排序正確”的段化,合并子數(shù)組,則整個數(shù)組已正確排序造成。

數(shù)組合并是整個過程中最復(fù)雜的地方显熏。可以想象手邊有兩堆撲克牌晒屎,每堆撲克牌都是從小到大排序完畢喘蟆,比較每堆撲克牌最上邊的牌的大小,取最小的放到手上鼓鲁,直到有一堆撲克牌已經(jīng)被取完蕴轨,此時再將剩下的那一堆撲克牌全部放到手上,則手中的所有牌都是按從小到大順序排列好骇吭。

為了界定每堆撲克牌是否已經(jīng)取完橙弱,本文中向每堆撲克牌最后放入一張哨兵牌。以便于節(jié)省大量的判斷燥狰,牌堆是否已經(jīng)取完棘脐。

  //p是數(shù)組中合并的起始index,p是中間位置龙致,r是末位
  public static void merge(int[] array, int p, int q, int r){
    int n1 = q - p + 1;
    int n2 = r - (q + 1) + 1;
    int[] left = new int[n1 + 1];
    int[] right = new int[n2 + 1];
    int i = 0;
    int j = 0;
    for (i = 0; i < n1; i++) {
        left[i] = array[p + i];
    }
    left[n1] = Integer.MAX_VALUE;
    for (j = 0; j < n2; j++) {
        right[j] = array[q + 1 + j];
    }
    right[n2] = Integer.MAX_VALUE;
    i = 0;
    j = 0;
    //哨兵牌為正整數(shù)最大值蛀缝,所以子數(shù)組中不可能有大于它的數(shù),假設(shè)left子數(shù)組已經(jīng)被取完目代,只剩下哨兵牌
    //right子數(shù)組剩下的元素則必然全都小于哨兵牌屈梁,則可全取right子數(shù)組嗤练,而不用判定子數(shù)組是否已經(jīng)取完
    for (int k = p; k <= r; k++) {
        if (left[i] < right[j]) {
            array[k] = left[i];
            i ++;
        }else {
            array[k] = right[j];
            j++;
        }
    }
}

最復(fù)雜的合并工作已經(jīng)完成,則分解子問題和求解子問題則很簡單了在讶。

  public static void sort(int[] array, int p, int r){
    if (p < r) {
        int q = (r + p)/2;
        sort(array, p, q);
        sort(array, q + 1, r);
        merge(array, p, q, r);
    }
}

連續(xù)子數(shù)組和最大值

如果在一個數(shù)組中煞抬,找出一個連續(xù)子數(shù)組和的最大值。此問題必須是在有負數(shù)的數(shù)組中才有意義构哺,如果數(shù)組中全為正數(shù)此疹,那么此問題的解即為數(shù)組所有元素之和

此問題也可以用分治算法解決,將數(shù)組分解為兩個子數(shù)組遮婶,那么問題的解必然為以下三個之一:

  • 左子數(shù)組中的連續(xù)子數(shù)組和最大值
  • 右子數(shù)組中的連續(xù)子數(shù)組和最大值
  • 包含跨越分隔左右子數(shù)組的中間值的連續(xù)子數(shù)組的和的最大值。

分治算法有三步湖笨,分解子問題旗扑、解決子問題、全并子問題慈省。本問題中臀防,合并子問題非常簡單,如果以上三個值已經(jīng)求出來边败,通過比較大小即可得知袱衷,時間復(fù)雜度為1,分解子問題和解決子問題中只有一步較為復(fù)雜笑窜,即是上文中的第三步致燥,求 包含中間值的連續(xù)子數(shù)組和的最大值,不過將此問題單獨提出來也是比較簡單的排截,因為此連續(xù)子數(shù)組必然包含中間值嫌蚤。

根據(jù)中間值將數(shù)組分成兩半,分別求取左右兩邊的連續(xù)子數(shù)組和的最大值断傲,再相加即可脱吱。時間復(fù)雜度為n。

  private static int[] getMaxSumSubArrayCorssMidel(int[] array, int low, int middle, int high){
    int left_sum = Integer.MIN_VALUE;
    int sum = 0;
    int maxLeftIndex = 0;
    for (int i = middle; i >= low; i--) {
        sum = sum + array[i];
        if (sum > left_sum) {
            left_sum = sum;
            maxLeftIndex = i;
        }
    }
    sum = 0;
    int right_sum = Integer.MIN_VALUE;
    int maxRightIndex = 0;
    for (int i = middle + 1; i <= high; i++) {
        sum = sum + array[i];
        if (sum > right_sum) {
            right_sum = sum;
            maxRightIndex = i;
        }
    }
    return new int[]{maxLeftIndex, maxRightIndex, left_sum + right_sum};
}

遞歸求解子問題的邊界點是什么呢认罩?數(shù)組只有一個元素箱蝠,則連續(xù)子數(shù)組和的最大值則為數(shù)組唯一元素。確定了邊界垦垂,則剩余代碼非常容易寫了

  private static int[] getMaxSumSubArray(int[] array, int low, int high){
    if (low == high) {
        return new int[]{low, high ,array[low]};
    }else {
        int middle = (low + high)/2;
        int[] left = getMaxSumSubArray(array, low, middle);
        int[] right = getMaxSumSubArray(array, middle + 1, high);
        int[] corss = getMaxSumSubArrayCorssMidel(array, low, middle, high);
        if (left[2] >= right[2] && left[2] >= corss[2]) {
            return left;
        }else if (right[2] >= left[2] && right[2] >= corss[2]) {
            return right;
        }else{
            return corss;
        }
    }
}

求取數(shù)組中元素的最大值

求取數(shù)組中元素的最大值也可以使用分治法解決宦搬,通常人們都使用二分法查找,后續(xù)將討論分治算法的時間復(fù)雜度乔外,可計算得出床三,二分法和分治算法的時間復(fù)雜度是一樣的。不過遞歸本身效率不高杨幼,執(zhí)行一次函數(shù)需要入棧出棧多次撇簿,分治算法的最終效率肯定是不及二分查找的聂渊。不過此文中只為展示分治算法的使用。

同理四瘫,將數(shù)組分成兩個子數(shù)組汉嗽,分別求取兩個子數(shù)組中的最大值,再將兩個子數(shù)組的最大值比較找蜜,取其大者饼暑,則可求得此問題的解。

本例中合并子問題非常簡單洗做,只需要比較子問題的解大小即可弓叛,取其大者就ok了,時間復(fù)雜度為1

  private static int findMax(int[] array, int p, int q){
    if (p == q) {
        return array[q];
    }else {
        int middle = (p + q)/2;
        int left = findMax(array, p, middle);
        int right = findMax(array, middle + 1, q);
        return (left > right) ? left : right;
    }
}

未完待續(xù)诚纸,關(guān)于分治算法的時間復(fù)雜度撰筷。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市畦徘,隨后出現(xiàn)的幾起案子毕籽,更是在濱河造成了極大的恐慌,老刑警劉巖井辆,帶你破解...
    沈念sama閱讀 217,084評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件关筒,死亡現(xiàn)場離奇詭異,居然都是意外死亡杯缺,警方通過查閱死者的電腦和手機蒸播,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來萍肆,“玉大人廉赔,你說我怎么就攤上這事∝遗福” “怎么了蜡塌?”我有些...
    開封第一講書人閱讀 163,450評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長勿负。 經(jīng)常有香客問我馏艾,道長,這世上最難降的妖魔是什么奴愉? 我笑而不...
    開封第一講書人閱讀 58,322評論 1 293
  • 正文 為了忘掉前任琅摩,我火速辦了婚禮,結(jié)果婚禮上锭硼,老公的妹妹穿的比我還像新娘房资。我一直安慰自己,他們只是感情好檀头,可當我...
    茶點故事閱讀 67,370評論 6 390
  • 文/花漫 我一把揭開白布轰异。 她就那樣靜靜地躺著岖沛,像睡著了一般。 火紅的嫁衣襯著肌膚如雪搭独。 梳的紋絲不亂的頭發(fā)上婴削,一...
    開封第一講書人閱讀 51,274評論 1 300
  • 那天,我揣著相機與錄音牙肝,去河邊找鬼唉俗。 笑死,一個胖子當著我的面吹牛配椭,可吹牛的內(nèi)容都是我干的虫溜。 我是一名探鬼主播,決...
    沈念sama閱讀 40,126評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼股缸,長吁一口氣:“原來是場噩夢啊……” “哼吼渡!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起乓序,我...
    開封第一講書人閱讀 38,980評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎坎背,沒想到半個月后替劈,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,414評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡得滤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,599評論 3 334
  • 正文 我和宋清朗相戀三年陨献,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片懂更。...
    茶點故事閱讀 39,773評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡眨业,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出沮协,到底是詐尸還是另有隱情龄捡,我是刑警寧澤,帶...
    沈念sama閱讀 35,470評論 5 344
  • 正文 年R本政府宣布慷暂,位于F島的核電站聘殖,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏行瑞。R本人自食惡果不足惜奸腺,卻給世界環(huán)境...
    茶點故事閱讀 41,080評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望血久。 院中可真熱鬧突照,春花似錦、人聲如沸氧吐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至衔肢,卻和暖如春庄岖,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背角骤。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評論 1 269
  • 我被黑心中介騙來泰國打工隅忿, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人邦尊。 一個月前我還...
    沈念sama閱讀 47,865評論 2 370
  • 正文 我出身青樓背桐,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蝉揍。 傳聞我的和親對象是個殘疾皇子链峭,可洞房花燭夜當晚...
    茶點故事閱讀 44,689評論 2 354

推薦閱讀更多精彩內(nèi)容