leetcode分治法

 

分治算法

 

一、基本概念

在计算机科学中,分治法是一种很重要的算法。字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。这个技巧是很多高效算法的基础,如排序算法(快速排序,归并排序),傅立叶变换(快速傅立叶变换)……

任何一个可以用计算机求解的问题所需的计算时间都与其规模有关。问题的规模越小,越容易直接求解,解题所需的计算时间也越少。例如,对于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;
    }
};

 

 

 

 

 

 

 

 

你可能感兴趣的:(leetcode)