算法导论实验一:分治法

算法导论实验一 分治法

用分治法求解数组的中位数和最大子集


``


前言

完成分治法的实践:用分治法求解数组的中位数和最大子集

一、实验原理

1.1 分治法的概念

分治算法的基本思想是将一个规模为N的问题,分解成K个规模较小子问题,这些子问题相互独立且与原问题性质相同。求解出子问题的解,合并得到原问题的解。分治法在每一层递归上有三个基本步骤:

  1. 分解:将原问题逐层分解为若干个较小规模,相互独立且与原问题表示形式相同的子问题;
  2. 求解:若分解后的子问题规模较小且求解容易则直接求解,否则将相应的子问题再不断分解为更小的子问题,直到容易求解为止;
  3. 合并:将已经求解出的各子问题的解逐步合并,最后得到原问题的解。

1.2 适用分治策略解决的问题

对于一个输入规模为n并且取值比较大的问题,若能满足下面条件,使用分治法的思想就可以提高解决问题的效率。

  1. 保证这n个数据能分解为k个不同的子集合,同时得到的k个子集合为可以独立求解的子问题(1 <= k <= n);
  2. 分解后的各子问题和原问题具有相似的表示形式,便于使用循环或递归机制;
  3. 得到这些子问题的解之后,可以方便递推出原问题的解。

二、实验要求

2.1 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))
利用分治法求两个已排序数组的中位数,同时是二分查找,即将空间复杂度压缩在O(log(m+n))上。

本题难度不小,主要就是采用二分法的方式来切割数组,找出第k(k为中位数的index)k = (m + n) / 2小的数字。因此在两个数组中不断寻找第k/2大的数字,并判断两个数字的大小,较小数字的那个数组中前k/2个数字必不可能是第k大的数字,再递归下去,直至k=1,或者遍历完一个数组,即可找到中位数。
当然,在本题中还有一些较为巧妙的代码。

  1. 不论两个数组之和是奇数或者偶数,设中位数左边数字下标leftIndex为 (size + 1)/ 2,中位数右边数字下标rightIndex为(size = 2) / 2,再用两个数字之和/2,即可得出中位数。简单举例测试即可证明该求法正确:
  • 举例一:即假设nums1.size = 3,nums2.size = 4,则leftIndex = 4,rightIndex = 4,正确。
  • 举例二:假设nums1.size = 5,nums2.size = 3,则leftIndex = 4,rightIndex = 5,即中位数为第四个和第五个大的数的平均数,正确。
  1. 在递归中,注意确认两个数组中剩下的长度,并判断较小的数组是否已遍历结束,

代码如下所示:

class Solution {
private:
    int getKth(vector<int>& nums1,int start1, int end1,vector<int>& nums2,int start2,int end2,int k){
        int len1 = end1 - start1 + 1;
        int len2 = end2 - start2 + 1;
        if(len1 > len2){
            return getKth(nums2,start2,end2,nums1,start1,end1,k);
        }
        if(len1 == 0){
            return nums2[start2 + k - 1];
        }
        if(k == 1){
            return min(nums1[start1],nums2[start2]);
        }
        int i = start1 + min(len1,k / 2) - 1;
        int j = start2 + min(len2,k / 2) - 1;
        if(nums1[i] > nums2[j]){
            return getKth(nums1,start1,end1,nums2,j + 1,end2,k - (j - start2 + 1));
        }
        else{
            return getKth(nums1, i + 1,end1,nums2,start2,end2,k - (i - start1 + 1));
        }
    }
public:

    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int n = nums1.size();
        int m = nums2.size();
        int leftMedian = (m + n + 1) / 2;
        int rightMedian = (m + n + 2) / 2;
        return (double)(getKth(nums1,0,n - 1,nums2,0,m - 1,leftMedian) + getKth(nums1,0,n-1,nums2,0,m-1,rightMedian)) / 2;
    }
};

代码是根据k来遍历,k的初始值为(m + n)/2,每次排除k/2个数,因此时间复杂度为O(log(m+n))
运行结果截图如下所示:
在这里插入图片描述
可以通过全部测试用例

2.2 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

该题难度不大,即分为三部分,最大子数组在midmid = (left + right) / 2的左边、右边或者经过mid三种情况,取三种情况的最大值即可。
代码如下:

class Solution {
public:
    
    int maxSubArray(vector<int>& nums) {
        int len = nums.size();
        if(len == 0){
            return 0;
        }
        return maxSubArraySum(nums,0,len - 1);
    }
    int maxSubArraySum(vector<int>& nums,int left,int right){
        if(left == right){
            return nums[left];
        }
        int mid = left + (right - left) / 2;
        return max(maxCrossingSum(nums,left,mid,right),max(maxSubArraySum(nums,left,mid),maxSubArraySum(nums,mid + 1,right)));
        
    }
    int maxCrossingSum(vector<int>& nums,int left,int mid,int right){
        int maxLeftSum = INT_MIN,maxRightSum = INT_MIN;
        int leftSum = 0,rightSum = 0;
        for(int i = mid;i >= left;i--){
            leftSum += nums[i];
            maxLeftSum = max(leftSum,maxLeftSum);
        }
        for(int i = mid + 1;i <= right;i++){
            rightSum += nums[i];
            maxRightSum = max(rightSum,maxRightSum);
        }
        return maxLeftSum + maxRightSum;
    }
};

由于每次的mid都是left + (right - left) / 2,因此时间复杂度为O(logm),m为该数组的大小size.
运行结果截图如下图所示:
在这里插入图片描述
可以通过全部测试用例


总结

分治法的使用,主要还是将问题切分为一个个子问题,看待子问题的角度很重要。本次实验中,第一题是将中位数看为是第k个大的数,并每次去掉k/2个数,直至k==1为止。第二次实验即将数组根据中间点进行划分,根据最大子数组经过不经过中间点来进行分治。当然,分治法的共同点还是使用递归的方法来解决问题,将问题分解为类似的子问题,解决问题,最后合并问题。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值