分治算法也是一种重要的求解问题的思路,移用百科的定义,分治就是分而治之,就是将一个规模比较大的问题分解为几个规模较小的问题,这些规模较小的问题是相互独立的,但是它们与原问题性质相同,可以用相同的思路进行继续分解求解,最后将这些小规模的问题求解出的解合并起来作为原问题的解,故而分治算法的求解过程为:
a、分解:寻找将原问题分解为求解几个规模比较小的问题,这些小问题和原问题的性值是相同的
b、求解:将问题规模分解到一定程度,可以用简单的方法进行求解
c、合并:将这些子问题的解合并起来作为原问题的解返回给调用者
因为子问题和原问题是同构的,所以分治在一定程度上可以借助递归算法进行实现,递归算法更像是一种模板。以下是可以用分治算法思想的案例:
1、归并排序
归并排序是将问题先分解,然后进行排序,最后将小规模的排好序的子解合并为一个原问题的解,详见https://www.cnblogs.com/codeMedita/p/7412568.html
2、求解最大值
给定一个无序的数组arr,求解arr中的最大值,可有两种计算:
a 直接遍历求解
def maxVal(arr):
int max=0;
for val in arr:
max=max>val?max:val;
return max
b 分治方法求解
def dividAndConquer(arr,leftIndex,rightIndex):
if(rightIndex==leftIndex+1 || rightIndex==leftIndex){
return Math.max(arr[leftIndex],arr[rightIndex]);
}
int mid=(leftIndex+rightIndex)/2;
int leftMax=dividAndConquer(arr,leftIndex,mid);
int rightMax=dividAndConquer(arr,mid,rightIndex);
return Math.max(leftMax,rightMax);
3、二分查找,给定一个从小到大有序的数组和一个值,求解改值在数组中的位置
def binarySearch(arr,leftIndex,rightIndex,val):
if(leftIndex>rightIndex) return false;
if(leftIndex==rightIndex):
return arr[leftIndex]==val;
int mid=(leftIndex+rightIndex)/2;
if(arr[mid]==val) return true;
if(arr[mid]>val){
if(leftIndex==mid) return false;
return binarySearch(arr,leftIndex,mid-1,val);
}
return binarySearch(arr,mid+1,right,val);
4、输出一条线上距离最近的两个点之间的距离值,给定一个数组arr,其值是从小到大有序的,输出最近点的距离
def minDistance(arr,left,right):
//同一个点返回极大值
if(arr[left]==arr[right]) return Integer.MAX
//相邻点的距离
if(right==left+1) return arr[right]-arr[left];
int mid=(left+right)/2;
//中间点的临近点的距离,这里很重要!!!
int midLeft=arr[mid]-arr[mid-1];
int midRight=arr[mid+1]-arr[mid];
//记录mid 附近两边的最小距离
midLeft=midLeft>midRight?midRight:midLeft;
int minLeft=minDistance(arr,left,mid);
int minRight=minDistance(arr,mid+1,right);
//记录mid 左侧和右侧的最小距离
minLeft=minLeft>minRight?minRight:minLeft;
//返回最小值
return midLeft>minLeft?minLeft:midLeft;
5、输出平面上距离最近的两个点的距离,给定一个点集,求出这个点集中最近两点的距离,这里需要进行一些排序操作,计算描述:
a 按横坐标递增的形式对点集进行排序,相同的横坐标的点击再用纵坐标从小大大进行排序,然后切分点集,切分方法mid=(left+right)/2
b 计算(left,mid)中的点集最小距离minleft,计算(mid,right)中的点集最小距离minright,此时mid-left<=3,right-mid<=3,如果
mid-left>3,则继续对(left,mid)中的点集进行切分,(mid,right)同理
c 计算mid+min(midleft,minright)之间的点击,按照遍历的方式进行计算
伪代码描述:
def minDis(point,left,right):
if(left+1==right):
//distance为预先定义好的计算方法
return distance(point[left],point[right]);
if(left+2==right):
dis1=distance(point[left],point[left+1]);
dis2=distance(point[left+1],point[right]);
dis3=distance(point[left],point[right]);
return min(dis1,min(dis2,dis3));
mid=(left+right)/2;
minleft=minDis(point,left,mid);
minright=minDis(point,mid+1,right);
//计算出mid点左右两侧中距离较短的点对距离
minMid=min(minleft,minright);
//以minMid为长度找出距离mid不超过minMid的点
for i=left to right:
if(Math.abs(point[mid].x-point[i])<d && mid!=i):
tempMemory.add(point[i]);
for i=0 to tempMemory.length:
for j=i+1 to tempMomory.length:
minMid=min(minMid,distance(point[i],point[j]);
return minMid;
java代码示例:
//这里未进行开方
static int distance(List<Node> nodes,int left,int right){ Node leftNode=nodes.get(left),rightNode=nodes.get(right); int dis=(leftNode.x-rightNode.x)*(leftNode.x-rightNode.x)+ (leftNode.y-rightNode.y)*(leftNode.y-rightNode.y); return dis; } static int minDistance(List<Node> nodes,int left,int right){ if(right==left){ return Integer.MAX_VALUE; } if(right-1==left){ return distance(nodes,left,right); } if(right-2==left){ int m1=distance(nodes,left,left+1); int m2=distance(nodes,left+1,right); int m3=distance(nodes,left,right); return Math.min(m1,Math.min(m2,m3)); } int mid=(left+right)>>1; int leftmid=minDistance(nodes,left,mid); int rightmid=minDistance(nodes,mid+1,right); int minMid=Math.min(leftmid,rightmid); List<Node> temp=new ArrayList<>(); for(int i=left;i<right;i++){ if(Math.abs(nodes.get(i).x-nodes.get(mid).x)<minMid){ temp.add(nodes.get(i)); } } int dis=0; for(int i=0;i<temp.size();i++){ for(int j=i+1;j<temp.size();j++){ dis=distance(nodes,i,j); minMid=minMid>dis?dis:minMid; } } return minMid; }
分治算法重要步骤:
1、分解:大的问题进行互不相干的小问题
2、解决:将小到一定规模的问题用简单的方法计算出结果
3、合并:将小问题的解进行整合作为原始问题的解返给调用方