自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

「五大常用算法」一文圖解分支算法和思想

開發(fā) 前端 算法
分治算法(divide and conquer)是五大常用算法(分治算法、動(dòng)態(tài)規(guī)劃算法、貪心算法、回溯法、分治界限法)之一,很多人在平時(shí)學(xué)習(xí)中可能只是知道分治算法,但是可能并沒有系統(tǒng)的學(xué)習(xí)分治算法,本篇就帶你較為全面的去認(rèn)識(shí)和了解分治算法。

[[355166]]

本文轉(zhuǎn)載自微信公眾號(hào)「bigsai」,作者bigsai 。轉(zhuǎn)載本文請(qǐng)聯(lián)系bigsai公眾號(hào)。

前言

分治算法(divide and conquer)是五大常用算法(分治算法、動(dòng)態(tài)規(guī)劃算法、貪心算法、回溯法、分治界限法)之一,很多人在平時(shí)學(xué)習(xí)中可能只是知道分治算法,但是可能并沒有系統(tǒng)的學(xué)習(xí)分治算法,本篇就帶你較為全面的去認(rèn)識(shí)和了解分治算法。

在學(xué)習(xí)分治算法之前,問你一個(gè)問題,相信大家小時(shí)候都有存錢罐的經(jīng)歷,父母親人如果給錢都會(huì)往自己的寶藏中存錢,我們每隔一段時(shí)間都會(huì)清點(diǎn)清點(diǎn)錢。但是一堆錢讓你處理起來你可能覺得很復(fù)雜,因?yàn)閿?shù)據(jù)相對(duì)于大腦有點(diǎn)龐大了,并且很容易算錯(cuò),你可能會(huì)將它先分成幾個(gè)小份算,然后再疊加起來計(jì)算總和就獲得這堆錢的總數(shù)了

 

當(dāng)然如果你覺得各個(gè)部分錢數(shù)量還是太大,你依然可以進(jìn)行劃分然后合并,我們之所以這么多是因?yàn)椋?/p>

計(jì)算每個(gè)小堆錢的方式和計(jì)算最大堆錢的方式是相同的(區(qū)別在于體量上)

然后大堆錢總和其實(shí)就是小堆錢結(jié)果之和。這樣其實(shí)就有一種分治的思想。

當(dāng)然這些錢都是想出來的……

分治算法介紹

分治算法是用了分治思想的一種算法,什么是分治?

分治,字面上的解釋是“分而治之”,就是把一個(gè)復(fù)雜的問題分成兩個(gè)或更多的相同或相似的子問題,再把子問題分成更小的子問題……直到最后子問題可以簡(jiǎn)單的直接求解,原問題的解即子問題的解的合并。在計(jì)算機(jī)科學(xué)中,分治法就是運(yùn)用分治思想的一種很重要的算法。分治法是很多高效算法的基礎(chǔ),如排序算法(快速排序,歸并排序),傅立葉變換(快速傅立葉變換)等等。

將父問題分解為子問題同等方式求解,這和遞歸的概念很吻合,所以在分治算法通常以遞歸的方式實(shí)現(xiàn)(當(dāng)然也有非遞歸的實(shí)現(xiàn)方式)。分治算法的描述從字面上也很容易理解,分、治其實(shí)還有個(gè)合并的過程:

  • 分(Divide):遞歸解決較小的問題(到終止層或者可以解決的時(shí)候停下)
  • 治(Conquer):遞歸求解,如果問題夠小直接求解。
  • 合并(Combine):將子問題的解構(gòu)建父類問題

一般分治算法在正文中分解為兩個(gè)及以上的遞歸調(diào)用,并且子類問題一般是不相交的(互不影響)。當(dāng)求解一個(gè)問題規(guī)模很大很難直接求解,但是規(guī)模較小的時(shí)候問題很容易求解并且這個(gè)問題并且問題滿足分治算法的適用條件,那么就可以使用分治算法。


那么采用分治算法解決的問題需要 滿足那些條件(特征) 呢?

1 . 原問題規(guī)模通常比較大,不易直接解決,但問題縮小到一定程度就能較容易的解決。

2 . 問題可以分解為若干規(guī)模較小、求解方式相同(似)的子問題。且子問題之間求解是獨(dú)立的互不影響。

3 . 合并問題分解的子問題可以得到問題的解。

你可能會(huì)疑惑分治算法和遞歸有什么關(guān)系?其實(shí)分治重要的是一種思想,注重的是問題分、治、合并的過程。而遞歸是一種方式(工具),這種方式通過方法自己調(diào)用自己形成一個(gè)來回的過程,而分治可能就是利用了多次這樣的來回過程。

分治算法經(jīng)典問題

對(duì)于分治算法的經(jīng)典問題,重要的是其思想,因?yàn)槲覀兇蟛糠纸柚f歸去實(shí)現(xiàn),所以在代碼實(shí)現(xiàn)上大部分都是很簡(jiǎn)單,而本篇也重在講述思想。

分治算法的經(jīng)典問題,個(gè)人將它分成兩大類:子問題完全獨(dú)立和子問題不完全獨(dú)立。

1 . 子問題完全獨(dú)立就是原問題的答案可完全由子問題的結(jié)果推出。

2 . 子問題不完全獨(dú)立,有些區(qū)間類的問題或者跨區(qū)間問題使用分治可能結(jié)果跨區(qū)間,在考慮問題的時(shí)候需要仔細(xì)借鑒下。

二分搜索

二分搜索是分治的一個(gè)實(shí)例,只不過二分搜索有著自己的特殊性

  • 序列有序
  • 結(jié)果為一個(gè)值

正常二分將一個(gè)完整的區(qū)間分成兩個(gè)區(qū)間,兩個(gè)區(qū)間本應(yīng)單獨(dú)找值然后確認(rèn)結(jié)果,但是通過有序的區(qū)間可以直接確定結(jié)果在哪個(gè)區(qū)間,所以分的兩個(gè)區(qū)間只需要計(jì)算其中一個(gè)區(qū)間,然后繼續(xù)進(jìn)行一直到結(jié)束。實(shí)現(xiàn)方式有遞歸和非遞歸,但是非遞歸用的更多一些:

  1. public int searchInsert(int[] nums, int target) { 
  2.   if(nums[0]>=target)return 0;//剪枝 
  3.   if(nums[nums.length-1]==target)return nums.length-1;//剪枝 
  4.   if(nums[nums.length-1]<target)return nums.length; 
  5.   int left=0,right=nums.length-1; 
  6.   while (left<right) { 
  7.     int mid=(left+right)/2; 
  8.     if(nums[mid]==target) 
  9.       return mid; 
  10.     else if (nums[mid]>target) { 
  11.       right=mid; 
  12.     } 
  13.     else { 
  14.       left=mid+1; 
  15.     } 
  16.   } 
  17.   return left

 快速排序

快排也是分治的一個(gè)實(shí)例,快排每一趟會(huì)選定一個(gè)數(shù),將比這個(gè)數(shù)小的放左面,比這個(gè)數(shù)大的放右面,然后遞歸分治求解兩個(gè)子區(qū)間,當(dāng)然快排因?yàn)樵诜值臅r(shí)候就做了很多工作,當(dāng)全部分到最底層的時(shí)候這個(gè)序列的值就是排序完的值。這是一種分而治之的體現(xiàn)。


  1. public void quicksort(int [] a,int left,int right
  2.   int low=left
  3.   int high=right
  4.   //下面兩句的順序一定不能混,否則會(huì)產(chǎn)生數(shù)組越界!?。ery important?。?! 
  5.   if(low>high)//作為判斷是否截止條件 
  6.     return
  7.   int k=a[low];//額外空間k,取最左側(cè)的一個(gè)作為衡量,最后要求左側(cè)都比它小,右側(cè)都比它大。 
  8.   while(low<high)//這一輪要求把左側(cè)小于a[low],右側(cè)大于a[low]。 
  9.   { 
  10.     while(low<high&&a[high]>=k)//右側(cè)找到第一個(gè)小于k的停止 
  11.     { 
  12.       high--; 
  13.     } 
  14.     //這樣就找到第一個(gè)比它小的了 
  15.     a[low]=a[high];//放到low位置 
  16.     while(low<high&&a[low]<=k)//在low往右找到第一個(gè)大于k的,放到右側(cè)a[high]位置 
  17.     { 
  18.       low++; 
  19.     } 
  20.     a[high]=a[low];    
  21.   } 
  22.   a[low]=k;//賦值然后左右遞歸分治求之 
  23.   quicksort(a, left, low-1); 
  24.   quicksort(a, low+1, right);   

 歸并排序(逆序數(shù))

快排在分的時(shí)候做了很多工作,而歸并就是相反,歸并在分的時(shí)候按照數(shù)量均勻分,而合并時(shí)候已經(jīng)是兩兩有序的進(jìn)行合并的,因?yàn)閮蓚€(gè)有序序列O(n)級(jí)別的復(fù)雜度即可得到需要的結(jié)果。而逆序數(shù)在歸并排序基礎(chǔ)上變形同樣也是分治思想求解。 

 
  1. private static void mergesort(int[] array, int leftint right) { 
  2.   int mid=(left+right)/2; 
  3.   if(left<right
  4.   { 
  5.     mergesort(array, left, mid); 
  6.     mergesort(array, mid+1, right); 
  7.     merge(array, left,mid, right); 
  8.   } 
  9.  
  10. private static void merge(int[] array, int l, int mid, int r) { 
  11.   int lindex=l;int rindex=mid+1; 
  12.   int team[]=new int[r-l+1]; 
  13.   int teamindex=0; 
  14.   while (lindex<=mid&&rindex<=r) {//先左右比較合并 
  15.     if(array[lindex]<=array[rindex]) 
  16.     { 
  17.       team[teamindex++]=array[lindex++]; 
  18.     } 
  19.     else {     
  20.       team[teamindex++]=array[rindex++]; 
  21.     } 
  22.   } 
  23.   while(lindex<=mid)//當(dāng)一個(gè)越界后剩余按序列添加即可 
  24.   { 
  25.     team[teamindex++]=array[lindex++]; 
  26.  
  27.   } 
  28.   while(rindex<=r) 
  29.   { 
  30.     team[teamindex++]=array[rindex++]; 
  31.   }  
  32.   for(int i=0;i<teamindex;i++) 
  33.   { 
  34.     array[l+i]=team[i]; 
  35.   } 

 最大子序列和

最大子序列和的問題我們可以使用動(dòng)態(tài)規(guī)劃的解法,但是也可以使用分治算法來解決問題,但是最大子序列和在合并的時(shí)候并不是簡(jiǎn)單的合并,因?yàn)樽有蛄泻蜕婕暗揭粋€(gè)長度的問題,所以正確結(jié)果不一定全在最左側(cè)或者最右側(cè),而可能出現(xiàn)結(jié)果的區(qū)域?yàn)椋?/p>

  • 完全在中間的左側(cè)
  • 完全在中間的右側(cè)
  • 包含中間左右兩個(gè)節(jié)點(diǎn)的一個(gè)序列

用一張圖可以表示為:


所以在具體考慮的時(shí)候需要將無法遞歸得到結(jié)果的中間那個(gè)最大值串的結(jié)果也算出來參與左側(cè)、右側(cè)值得比較。

力扣53. 最大子序和在實(shí)現(xiàn)的代碼為:

  1. ublic int maxSubArray(int[] nums) { 
  2.     int max=maxsub(nums,0,nums.length-1); 
  3.     return max
  4. int maxsub(int nums[],int left,int right
  5.     if(left==right
  6.         return  nums[left]; 
  7.     int mid=(left+right)/2; 
  8.     int leftmax=maxsub(nums,left,mid);//左側(cè)最大 
  9.     int rightmax=maxsub(nums,mid+1,right);//右側(cè)最大 
  10.  
  11.     int midleft=nums[mid];//中間往左 
  12.     int midright=nums[mid+1];//中間往右 
  13.     int team=0; 
  14.     for(int i=mid;i>=left;i--) 
  15.     { 
  16.         team+=nums[i]; 
  17.         if(team>midleft) 
  18.             midleft=team; 
  19.     } 
  20.     team=0; 
  21.     for(int i=mid+1;i<=right;i++) 
  22.     { 
  23.         team+=nums[i]; 
  24.         if(team>midright) 
  25.             midright=team; 
  26.     } 
  27.     int max=midleft+midright;//中間的最大值 
  28.     if(max<leftmax) 
  29.         max=leftmax; 
  30.     if(max<rightmax) 
  31.         max=rightmax; 
  32.     return  max

 最近點(diǎn)對(duì)

最近點(diǎn)對(duì)是一個(gè)分治非常成功的運(yùn)用之一。在二維坐標(biāo)軸上有若干個(gè)點(diǎn)坐標(biāo),讓你求出最近的兩個(gè)點(diǎn)的距離,如果讓你直接求那么枚舉暴力是個(gè)非常非常大的計(jì)算量,我們通常采用分治的方法來優(yōu)化這種問題。


如果直接分成兩部分分治計(jì)算你肯定會(huì)發(fā)現(xiàn)如果最短的如果一個(gè)在左一個(gè)在右會(huì)出現(xiàn)問題。我們可以優(yōu)化一下。

在具體的優(yōu)化方案上,按照x或者y的維度進(jìn)行考慮,將數(shù)據(jù)分成兩個(gè)區(qū)域,先分別計(jì)算(按照同方法)左右區(qū)域內(nèi)最短的點(diǎn)對(duì)。然后根據(jù)這個(gè)兩個(gè)中較短的距離向左和向右覆蓋,計(jì)算被覆蓋的左右點(diǎn)之間的距離,找到最小那個(gè)距離與當(dāng)前最短距離比較即可。


這樣你就可以發(fā)現(xiàn)就這個(gè)一次的操作(不考慮的情況),左側(cè)紅點(diǎn)就避免和右側(cè)大部分紅點(diǎn)進(jìn)行距離計(jì)算(O(n2)的時(shí)間復(fù)雜度)。事實(shí)上,在進(jìn)行左右區(qū)間內(nèi)部計(jì)算的時(shí)候,它其實(shí)也這樣遞歸的進(jìn)行很多次分治計(jì)算。如圖所示:

 

這樣下去就可以節(jié)省很多次的計(jì)算量。

但是這種分治會(huì)存在一種問題就是二維坐標(biāo)可能點(diǎn)都聚集某個(gè)方法某條軸那么可能效果并不明顯(點(diǎn)都在x=2附近對(duì)x分割作用就不大),需要注意一下。

杭電1007推薦給大家,ac的代碼為:

  1. import java.io.BufferedReader; 
  2. import java.io.IOException; 
  3. import java.io.InputStreamReader; 
  4. import java.io.OutputStreamWriter; 
  5. import java.io.PrintWriter; 
  6. import java.io.StreamTokenizer; 
  7. import java.util.ArrayList; 
  8. import java.util.Arrays; 
  9. import java.util.Comparator; 
  10. import java.util.List; 
  11. public class Main { 
  12.     static int n; 
  13.     public static void main(String[] args) throws IOException { 
  14.         StreamTokenizer in=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in))); 
  15.         PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); 
  16.         //List<node>list=new ArrayList(); 
  17.          while(in.nextToken()!=StreamTokenizer.TT_EOF) 
  18.          { 
  19.              n=(int)in.nval;if(n==0) {break;} 
  20.             node no[]=new node[n]; 
  21.              
  22.              for(int i=0;i<n;i++) 
  23.              { 
  24.                  in.nextToken();double x=in.nval; 
  25.                  in.nextToken();double y=in.nval; 
  26.                 // list.add(new node(x,y)); 
  27.                  no[i]=new node(x,y); 
  28.              } 
  29.              Arrays.sort(no, com); 
  30.             double min= search(no,0,n-1); 
  31.             out.println(String.format("%.2f", Math.sqrt(min)/2));out.flush(); 
  32.          }          
  33.     } 
  34.     private static double search(node[] noint left,int right) { 
  35.         int mid=(right+left)/2; 
  36.         double minleng=0; 
  37.         if(left==right) {return Double.MAX_VALUE;} 
  38.         else if(left+1==right) {minleng= (no[left].x-no[right].x)*(no[left].x-no[right].x)+(no[left].y-no[right].y)*(no[left].y-no[right].y);} 
  39.         else minleng= min(search(no,left,mid),search(no,mid,right)); 
  40.         int ll=mid;int rr=mid+1; 
  41.         while(no[mid].y-no[ll].y<=Math.sqrt(minleng)/2&&ll-1>=left) {ll--;} 
  42.         while(no[rr].y-no[mid].y<=Math.sqrt(minleng)/2&&rr+1<=right) {rr++;} 
  43.         for(int i=ll;i<rr;i++) 
  44.         { 
  45.             for(int j=i+1;j<rr+1;j++) 
  46.             { 
  47.                 double team=0; 
  48.                 if(Math.abs((no[i].x-no[j].x)*(no[i].x-no[j].x))>minleng) {continue;} 
  49.                 else 
  50.                 {  
  51.                     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); 
  52.                     if(team<minleng)minleng=team; 
  53.                 } 
  54.             } 
  55.         } 
  56.         return minleng; 
  57.      
  58.     } 
  59.     private static double min(double a, double b) { 
  60.         // TODO 自動(dòng)生成的方法存根 
  61.         return a<b?a:b; 
  62.     } 
  63.     static Comparator<node>com=new Comparator<node>() { 
  64.  
  65.         @Override 
  66.         public int compare(node a1, node a2) { 
  67.             // TODO 自動(dòng)生成的方法存根 
  68.             return a1.y-a2.y>0?1:-1; 
  69.         }}; 
  70.     static class node 
  71.     { 
  72.         double x; 
  73.         double y; 
  74.         public node(double x,double y) 
  75.         { 
  76.             this.x=x; 
  77.             this.y=y; 
  78.         } 
  79.     } 

 結(jié)語

到這里,分治算法就講這么多了,因?yàn)榉种嗡惴ㄖ匾谟诶斫馄渌枷?,還有一些典型的分治算法解決的問題,例如大整數(shù)乘法、Strassen矩陣乘法、棋盤覆蓋、線性時(shí)間選擇、循環(huán)賽日程表、漢諾塔等問題你可以自己研究其分治的思想和原理。

原文鏈接:https://mp.weixin.qq.com/s/jHiBOjfyMvuvGzSecsdNPw

 

責(zé)任編輯:姜華 來源: bigsai
相關(guān)推薦

2020-10-14 10:21:02

算法算法思想數(shù)據(jù)

2018-12-26 08:00:00

CSS前端

2022-03-22 10:30:42

機(jī)器學(xué)習(xí)人工智能算法

2017-05-15 11:10:10

大數(shù)據(jù)聚類算法

2019-03-27 09:00:00

人工智能AI算法

2021-08-31 07:02:20

Diff算法DOM

2023-03-03 08:26:32

負(fù)載均衡算法服務(wù)

2020-11-13 15:39:16

數(shù)字化轉(zhuǎn)型IT數(shù)據(jù)

2023-02-01 09:56:55

2022-09-04 19:38:11

機(jī)器學(xué)習(xí)算法

2010-01-06 15:26:14

JSON語法

2022-03-28 10:03:58

二分查找算法

2022-03-14 08:01:06

LRU算法線程池

2024-03-14 10:38:49

開源框架

2011-03-16 10:19:21

瀏覽器性能測(cè)試

2010-06-12 16:42:03

UML設(shè)計(jì)

2019-01-11 18:52:35

深度學(xué)習(xí)視覺技術(shù)人工智能

2022-01-06 07:45:44

機(jī)器學(xué)習(xí)算法思路

2020-01-22 16:50:32

區(qū)塊鏈技術(shù)智能

2022-10-12 07:24:18

大文件哈希算法Hash
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)