五大常用算法(一) - 分治算法
分治算法
基本思想
分治法的設計思想是:將一個難以直接解決的大問題,分割成一些規模較小的相同問題,以便各個擊破,分而治之。
分治策略是:對于一個規模為n的問題,若該問題可以容易地解決(比如說規模n較小)則直接解決,否則將其分解為k個規模較小的子問題,這些子問題互相獨立且與原問題類型一致,遞歸地解這些子問題,然后將各子問題的解合并得到原問題的解。
分治是很多高效算法的基礎,如排序算法(快速排序,歸并排序),傅立葉變換(快速傅立葉變換)……
分治與遞歸像一對孿生兄弟,經常同時應用在算法設計之中,并由此產生許多高效算法。
使用場景
分治法所能解決的問題一般具有以下幾個特征:
1) 該問題的規模縮小到一定的程度就可以容易地解決
2) 該問題可以分解為若干個規模較小的相同問題,即該問題具有最優子結構性質。
3) 利用該問題分解出的子問題的解可以合并為該問題的解;
4) 該問題所分解出的各個子問題是相互獨立的,即子問題之間不包含公共的子子問題。
第一條特征是絕大多數問題都可以滿足的,因為問題的計算復雜性一般是隨著問題規模的增加而增加;
第二條特征是應用分治法的前提它也是大多數問題可以滿足的,此特征反映了遞歸思想的應用;
第三條特征是關鍵,能否利用分治法完全取決于問題是否具有第三條特征,如果具備了第一條和第二條特征,而不具備第三條特征,則可以考慮用貪心法或動態規劃法。
第四條特征涉及到分治法的效率,如果各子問題是不獨立的,則分治法要做許多不必要的工作,重復地解公共的子問題,此時雖然可用分治法,但一般用動態規劃法較好。
實例
可使用分治法求解的一些經典問題:
二分搜索;合并排序、快速排序;大整數乘法、Strassen矩陣乘法;棋盤覆蓋;線性時間選擇;最接近點對問題;循環賽日程表;漢諾塔
二分查找
二分查找(基于有序數組)
/*基于遞歸的二分查找*/ public int rank(Key key,int lo,int hi) { if(hi<lo)return lo;int mid = lo+(hi-lo)/2;int cmp = key.compareTo(keys[mid]);if(cmp > 0)return rank(key, mid+1, hi);else if(cmp < 0)return rank(key, lo, mid-1);else return mid; }漢諾塔
在漢諾塔游戲中,有三個分別命名為A、B、C的塔座,幾個大小各不相同,從小到大依次編號得圓盤。最初,所有得圓盤都在A塔座上,其中最大得圓盤在最下面,然后是第二大,以此類推。
游戲的目的是 將所有的圓盤從塔座A移動到塔座B,塔座C用來放置臨時圓盤,游戲的規則如下:
1、一次只能移動一個圓盤
2、任何時候都不能將一個較大的圓盤壓在較小的圓盤上面.
3、除了第二條限制,任何塔座的最上面的圓盤都可以移動到其他塔座上
漢諾塔問題解決思想:
在解決漢諾塔問題時,事實上我們不是最關心圓盤1開始應該挪到哪個塔座上,而是關心最下面的圓盤4。 當然,我們不能直接移動圓盤4,但是圓盤4最終將從塔座A移動到塔座B。按照游戲規則,在移動圓盤4之前的情況一定如下圖。
四個圓盤從A移動到B,就要考慮如何將前三個圓盤從A移動到C,然后將圓盤4從A移動到B,最后將前三個圓盤從C移動到B。
但是上面的步驟可以重復利用!將問題規模縮小:
三個圓盤從A移動到C,就要考慮如何將前兩個圓盤從A移動到B,然后將圓盤3從A移動到C,最后將前兩個圓盤從B移動到C。
持續簡化這個問題,最終我們將只需要處理一個圓盤從一個塔座移動到另一個塔座的問題。
public class Moved {private static int count = 1;public static void main(String[] args) {moved(4, "第一根柱子", "第二根柱子", "第三根柱子");}/*** @param i 圓盤數量* @param a 圓盤初始所在塔座* @param b 圓盤將要移動到的塔座* @param c 輔助圓盤移動的塔座*/public static void moved(int i, String a, String b, String c){if(i == 1){disPaly(1, a, b);}else{moved(i-1, a, c, b); //將i-1根圓盤由A移動到CdisPaly(i, a, b); //將圓盤i由A移動到Bmoved(i-1, c, b, a); //將i-1根圓盤由C移動到B}}public static void disPaly(int i,String a,String b){System.out.println("第"+count+"步:移動第"+i+"個塔從"+a+"到"+b);count++;} }運行結果:
第1步:移動第1個塔從第一根柱子到第三根柱子 第2步:移動第2個塔從第一根柱子到第二根柱子 第3步:移動第1個塔從第三根柱子到第二根柱子 第4步:移動第3個塔從第一根柱子到第三根柱子 第5步:移動第1個塔從第二根柱子到第一根柱子 第6步:移動第2個塔從第二根柱子到第三根柱子 第7步:移動第1個塔從第一根柱子到第三根柱子 第8步:移動第4個塔從第一根柱子到第二根柱子 第9步:移動第1個塔從第三根柱子到第二根柱子 第10步:移動第2個塔從第三根柱子到第一根柱子 第11步:移動第1個塔從第二根柱子到第一根柱子 第12步:移動第3個塔從第三根柱子到第二根柱子 第13步:移動第1個塔從第一根柱子到第三根柱子 第14步:移動第2個塔從第一根柱子到第二根柱子 第15步:移動第1個塔從第三根柱子到第二根柱子論文參考分治算法
總結
以上是生活随笔為你收集整理的五大常用算法(一) - 分治算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c++单链表【构造函数、运算符重载、析构
- 下一篇: 842. Split Array int