分治算法
一、基本概念
在计算机科学中,分治法是一种很重要的算法。字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。这个技巧是很多高效算法的基础,如排序算法(快速排序,归并排序),傅立叶变换(快速傅立叶变换)……
任何一个可以用计算机求解的问题所需的计算时间都与其规模有关。问题的规模越小,越容易直接求解,解题所需的计算时间也越少。例如,对于n个元素的排序问题,当n=1时,不需任何计算。n=2时,只要作一次比较即可排好序。n=3时只要作3次比较即可,…。而当n较大时,问题就不那么容易处理了。要想直接解决一个规模较大的问题,有时是相当困难的。
--------------------------------------------------------------------------------
二、基本思想及策略
分治法的设计思想是:将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。
分治策略是:对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小)则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。这种算法设计策略叫做分治法。
如果原问题可分割成k个子问题,1
--------------------------------------------------------------------------------
三、分治法适用的情况
分治法所能解决的问题一般具有以下几个特征:
1) 该问题的规模缩小到一定的程度就可以容易地解决
2) 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质。
3) 利用该问题分解出的子问题的解可以合并为该问题的解;
4) 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子子问题。
第一条特征是绝大多数问题都可以满足的,因为问题的计算复杂性一般是随着问题规模的增加而增加;
第二条特征是应用分治法的前提它也是大多数问题可以满足的,此特征反映了递归思想的应用;、
第三条特征是关键,能否利用分治法完全取决于问题是否具有第三条特征,如果具备了第一条和第二条特征,而不具备第三条特征,则可以考虑用贪心法或动态规划法。
第四条特征涉及到分治法的效率,如果各子问题是不独立的则分治法要做许多不必要的工作,重复地解公共的子问题,此时虽然可用分治法,但一般用动态规划法较好。
--------------------------------------------------------------------------------
四、分治法的基本步骤
分治法在每一层递归上都有三个步骤:
step1 分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;
step2 解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题
step3 合并:将各个子问题的解合并为原问题的解。
它的一般的算法设计模式如下:
Divide-and-Conquer(P)
1. if |P|≤n0
2. then return(ADHOC(P))
3. 将P分解为较小的子问题 P1 ,P2 ,...,Pk
4. for i←1 to k
5. do yi ← Divide-and-Conquer(Pi) △ 递归解决Pi
6. T ← MERGE(y1,y2,...,yk) △ 合并子问题
7. return(T)
其中|P|表示问题P的规模;n0为一阈值,表示当问题P的规模不超过n0时,问题已容易直接解出,不必再继续分解。ADHOC(P)是该分治法中的基本子算法,用于直接解小规模的问题P。因此,当P的规模不超过n0时直接用算法ADHOC(P)求解。算法MERGE(y1,y2,...,yk)是该分治法中的合并子算法,用于将P的子问题P1 ,P2 ,...,Pk的相应的解y1,y2,...,yk合并为P的解。
以上摘于http://blog.csdn.net/EbowTang/article/details/51218500
以下为leetcode中遇到的分治法相关的题
Median of Two Sorted Arrays
There are two sorted arrays nums1 and nums2 of size m and n respectively.
Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).
我的代码:
class Solution {
public:
double findMedianSortedArrays(vector& nums1, vector& nums2) {
for(int i=0;i
这个代码好像有点作弊嫌疑,就不算做出来了吧。
答案:
class Solution {
public:
double findkth(vector &A,vector& B,int A_st,int B_st,int k){
if(A_st>=A.size()){ //|P|≤n0
return B[B_st+k-1];
}
if(B_st >= B.size()){ //|P|≤n0
return A[A_st+k-1];
}
if(k==1) return min(A[A_st],B[B_st]); //|P|≤n0
int a_key=A_st+k/2-1>=A.size()?INT_MAX:A[A_st+k/2-1];
int b_key=B_st+k/2-1>=B.size()?INT_MAX:B[B_st+k/2-1];
if(a_key& nums1, vector& nums2) {
int sum = nums1.size() + nums2.size();
double ret;
if (sum & 1) {
ret = findkth(nums1, nums2, 0, 0, sum / 2 + 1);
} else {
ret = ((findkth(nums1, nums2, 0, 0, sum / 2)) + findkth(nums1, nums2, 0, 0, sum / 2 + 1)) / 2.0;
}
return ret;
}
};
将找两个集合的中位数问题扩大成找两个集合的第k小元素。解决一个问题时,我们可以把这个问题看作某个问题的子问题,当我们结解决了更大的问题时,子问题也就迎刃而解了。构建findkth函数是这道题的关键。用分治法构建findkth函数时,要考虑以下几点:
1 边界情况,分为集合的边界情况和k的边界情况。即某个集合为空时,以及k==1时。
2 转换为更小规模问题,此问题即为a_key
下一题
Maximum Subarray
Find the contiguous subarray within an array (containing at least one number) which has the largest sum.
For example, given the array [-2,1,-3,4,-1,2,1,-5,4]
,
the contiguous subarray [4,-1,2,1]
has the largest sum = 6
.
mx (largest sum of this subarray), lmx(largest sum starting from the left most element), rmx(largest sum ending with the right most element), sum(the sum of the total subarray).
class Solution {
public:
void maxSubArray(vector<int>& nums, int l, int r, int& mx, int& lmx, int& rmx, int& sum) {
if (l == r) { //可直接求解的最小规模
mx = lmx = rmx = sum = nums[l];
}
else {
int m = (l + r) / 2;
int mx1, lmx1, rmx1, sum1;
int mx2, lmx2, rmx2, sum2;
maxSubArray(nums, l, m, mx1, lmx1, rmx1, sum1);
maxSubArray(nums, m + 1, r, mx2, lmx2, rmx2, sum2);//此题即为子问题互相不独立,存在公共子问题
mx = max(max(mx1, mx2), rmx1 + lmx2);
lmx = max(lmx1, sum1 + lmx2);
rmx = max(rmx2, sum2 + rmx1);
sum = sum1 + sum2;
}
}
int maxSubArray(vector<int>& nums) {
if (nums.size() == 0) {
return 0;
}
int mx, lmx, rmx, sum;
maxSubArray(nums, 0, nums.size() - 1, mx, lmx, rmx, sum);
return mx;
}
};
Majority Element
Given an array of size n, find the majority element. The majority element is the element that appears more than ⌊ n/2 ⌋
times.
You may assume that the array is non-empty and the majority element always exist in the array.
class Solution {
public:
int majorityElement(vector& nums) {
return majority(nums,0,nums.size()-1);
}
int majority(vector& nums,int left,int right){
if(left==right) return nums[left];//最小规模时
int mid=((right-left)>>1);
int lm=majority(nums,left,left+mid);//子问题分解为两个子问题
int rm=majority(nums,left+mid+1,right);
if(lm==rm) return lm;//合并子问题
return count(nums.begin()+left,nums.begin()+right+1,lm)>count(nums.begin()+left,nums.begin()+right+1,rm)?lm:rm;
}
};