「五大常用算法」一文搞懂分治算法

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":5}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首發公衆號:bigsai 文章收錄在 ","attrs":{}},{"type":"link","attrs":{"href":"https://github.com/javasmall/bigsai-algorithm","title":""},"content":[{"type":"text","text":"bigsai-algorithm","attrs":{}}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"前言","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"分治算法(divide and conquer)是五大常用算法(分治算法、動態規劃算法、貪心算法、回溯法、分治界限法)之一,很多人在平時學習中可能只是知道分治算法,但是可能並沒有系統的學習分治算法,本篇就帶你較爲全面的去認識和了解分治算法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在學習分治算法之前,問你一個問題,相信大家小時候都有存錢罐的經歷,父母親人如果給錢都會往自己的寶藏中存錢,我們每隔一段時間都會清點清點錢。但是一堆錢讓你處理起來你可能覺得很複雜,因爲數據相對於大腦有點龐大了,並且很容易算錯,你可能會將它","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"先分","attrs":{}},{"type":"text","text":"成幾個小份算,然後","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"再疊加","attrs":{}},{"type":"text","text":"起來計算總和就獲得這堆錢的總數了","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/2c/2c8e857c032ef2400214548a6ef1a9c8.png","alt":"image-20201130124009617","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當然如果你覺得各個部分錢數量還是太大,你依然可以進行劃分然後合併,我們之所以這麼多是因爲:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"計算每個小堆錢的方式和計算最大堆錢的方式是相同的(區別在於體量上)","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後大堆錢總和其實就是小堆錢結果之和。這樣其實就有一種分治的思想。","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當然這些錢都是想出來的……","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" ","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ba/bacdb95df648e67cf0576a009697ebd2.gif","alt":"BACDB95DF648E67CF0576A009697EBD2","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"分治算法介紹","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"分治算法是用了分治思想的一種算法,什麼是分治","attrs":{}},{"type":"text","text":"?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"分治,字面上的解釋是“","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"分而治之","attrs":{}},{"type":"text","text":"”,就是把一個複雜的問題分成兩個或更多的相同或相似的子問題,再把子問題分成更小的子問題……直到最後子問題可以簡單的直接求解,原問題的解即子問題的解的合併。在計算機科學中,分治法就是運用分治思想的一種很重要的算法。分治法是很多高效算法的基礎,如排序算法(快速排序,歸併排序),傅立葉變換(快速傅立葉變換)等等。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"將父問題分解爲子問題同等方式求解,這和遞歸的概念很吻合,所以在分治算法通常以遞歸的方式實現(當然也有非遞歸的實現方式)。分治算法的描述從字面上也很容易理解,分、治其實還有個合併的過程:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"分(Divide):遞歸解決較小的問題(到終止層或者可以解決的時候停下)","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"治(Conquer):遞歸求解,如果問題夠小直接求解。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"合併(Combine):將子問題的解構建父類問題","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一般分治算法在正文中分解爲兩個即以上的遞歸調用,並且子類問題一般是不想交的(互不影響)。當求解一個問題規模很大很難直接求解,但是規模較小的時候問題很容易求解並且這個問題並且問題滿足分治算法的適用條件,那麼就可以使用分治算法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b9/b9a1a42fce769096c9440d94b23e4f8d.png","alt":"image-20201130165303362","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那麼採用分治算法解決的問題需要 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"滿足那些條件(特徵)","attrs":{}},{"type":"text","text":" 呢?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1 . 原問題規模通常比較大,不易直接解決,但問題縮小到一定程度就能較容易的解決。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2 . 問題可以分解爲若干規模較小、求解方式相同(似)的子問題。且子問題之間求解是獨立的互不影響。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3 . 合併問題分解的子問題可以得到問題的解。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你可能會疑惑分治算法和遞歸有什麼關係?其實分治重要的是一種思想,注重的是問題分、治、合併的過程。而遞歸是一種方式(工具),這種方式通過方法自己調用自己形成一個來回的過程,而分治可能就是利用了多次這樣的來回過程。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"分治算法經典問題","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於分治算法的經典問題,重要的是其思想,因爲我們大部分藉助遞歸去實現,所以在代碼實現上大部分都是很簡單,而本篇也重在講述思想。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"分治算法的經典問題,個人將它分成兩大類:子問題完全獨立和子問題不完全獨立。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1 . 子問題完全獨立就是原問題的答案可完全由子問題的結果推出。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2 . 子問題不完全獨立,有些區間類的問題或者跨區間問題使用分治可能結果跨區間,在考慮問題的時候需要仔細借鑑下。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"二分搜索","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"二分搜索是分治的一個實例,只不過二分搜索有着自己的特殊性","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"序列有序","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"結果爲一個值","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"正常二分將一個完整的區間分成兩個區間,兩個區間本應單獨找值然後確認結果,但是通過有序的區間可以直接確定結果在那個區間,所以分的兩個區間只需要計算其中一個區間,然後繼續進行一直到結束。實現方式有遞歸和非遞歸,但是非遞歸用的更多一些:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public int searchInsert(int[] nums, int target) {\n if(nums[0]>=target)return 0;//剪枝\n if(nums[nums.length-1]==target)return nums.length-1;//剪枝\n if(nums[nums.length-1]target) {\n right=mid;\n }\n else {\n left=mid+1;\n }\n }\n return left;\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"快速排序","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"快排也是分治的一個實例,快排每一趟會選定一個數,將比這個數小的放左面,比這個數大的放右面,然後遞歸分治求解兩個子區間,當然快排因爲在分的時候就做了很多工作,當全部分到最底層的時候這個序列的值就是排序完的值。這是一種分而治之的體現。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d0/d08a4e62e14fe7629e7230247d2e01a0.png","alt":"image-20201120133851275","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public void quicksort(int [] a,int left,int right)\n{\n int low=left;\n int high=right;\n //下面兩句的順序一定不能混,否則會產生數組越界!!!very important!!!\n if(low>high)//作爲判斷是否截止條件\n return;\n int k=a[low];//額外空間k,取最左側的一個作爲衡量,最後要求左側都比它小,右側都比它大。\n while(low=k)//右側找到第一個小於k的停止\n {\n high--;\n }\n //這樣就找到第一個比它小的了\n a[low]=a[high];//放到low位置\n while(low=left;i--)\n {\n team+=nums[i];\n if(team>midleft)\n midleft=team;\n }\n team=0;\n for(int i=mid+1;i<=right;i++)\n {\n team+=nums[i];\n if(team>midright)\n midright=team;\n }\n int max=midleft+midright;//中間的最大值\n if(maxlist=new ArrayList();\n while(in.nextToken()!=StreamTokenizer.TT_EOF)\n {\n n=(int)in.nval;if(n==0) {break;}\n node no[]=new node[n];\n \n for(int i=0;i=left) {ll--;}\n while(no[rr].y-no[mid].y<=Math.sqrt(minleng)/2&&rr+1<=right) {rr++;}\n for(int i=ll;iminleng) {continue;}\n else\n { \n team=(no[i].x-no[j].x)*(no[i].x-no[j].x)+(no[i].y-no[j].y)*(no[i].y-no[j].y);\n if(teamcom=new Comparator() {\n\n @Override\n public int compare(node a1, node a2) {\n // TODO 自動生成的方法存根\n return a1.y-a2.y>0?1:-1;\n }};\n static class node\n {\n double x;\n double y;\n public node(double x,double y)\n {\n this.x=x;\n this.y=y;\n }\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"結語","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"到這裏,分治算法就講這麼多了,因爲分治算法重要在於理解其思想,還有一些典型的分治算法解決的問題,例如大整數乘法、Strassen矩陣乘法、棋盤覆蓋、線性時間選擇、循環賽日程表、漢諾塔等問題你可以自己研究其分治的思想和原理。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原創不易,bigsai請你幫兩件事幫忙一下:","attrs":{}}]},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"點贊在看, 您的肯定是我在info創作的源源動力。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"微信搜索「","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"bigsai","attrs":{}},{"type":"text","text":"」,關注我的公衆號,不僅免費送你電子書,我還會第一時間在公衆號分享知識技術。加我還可拉你進力扣打卡羣一起打卡LeetCode。","attrs":{}}]}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"記得關注、咱們下次再見!","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/7a/7ad80742f5f11df1d65abef2010c72c6.png","alt":"image-20201114211553660","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章