【分治算法】

分治(Divide And Conquer)

分治,也就是分而治之。它的一般步骤是:

① 将原问题分解成若干个规模较小的子问题(子问题和原问题的结构一样,只是规模不一样)
② 子问题又不断分解成规模更小的子问题,直到不能再分解(直到可以轻易计算出子问题的解)
③ 利用子问题的解推导出原问题的解
在这里插入图片描述
因此,分治策略非常适合用递归。
需要注意的是:子问题之间是相互独立的

分治的应用:

  • 快速排序
  • 归并排序
  • Karatsuba 算法(大数乘法)

主定理(Master Theorem)

在这里插入图片描述
对n个数据排序:O(n2)
那么对n/2个数据排序:O(n2/4)
分治: O(n2/4) * 2 + merge
只需要保证merge< O(n2/2) 即可

问题1:最大连续子序列和

题目:leetcode_53_最大子序和
给定一个长度为 n 的整数序列,求它的最大连续子序列

比如 –2、1、–3、4、–1、2、1、–5、4 的最大连续子序列和是 4 + (–1) + 2 + 1 = 6

这道题也属于最大切片问题(最大区段,Greatest Slice)

概念区分

  • 连续子序列、子串、子数组、子区间:必须是连续的
  • 子序列:可以不连续

解法1 – 暴力出奇迹

穷举出所有可能的连续子序列,分别计算出它们的和,最后取它们中的最大值。

package 分治算法;

public class Main {

    public static void main(String[] args) {
        int[] nums = {-2,1,-3,4,-1,2,1,-5,4};
        System.out.println(maxSubArray(nums));
    }
    static int maxSubArray(int [] nums) {
        if (nums == null || nums.length == 0) return 0;
        // 这里注意, 容易写成 int max = 0, 可能会出错, max 默认值必须是最小的值
        int max = Integer.MIN_VALUE;
        // 穷举, 列出所有可能的连续子序列, 分别计算它们的和, 最后取出最大值
        for (int begin = 0; begin < nums.length; begin++) {
            for (int end = begin; end < nums.length; end++) {
                int sum = 0;
                for (int i = begin; i <= end ; i++) {
                    sum+= nums[i];
                }
                if(sum > max) max = sum;
            }
        }
        return max;
    }

}

优化1:减少重复计算

算法缺陷:重复计算太多!
[begin,end] 和 [begin,end+1]重复计算了[begin,end],现在考虑优化,让[begin,end]的值被记住

  //todo 优化1 减去重复计算  [begin,end] 的值作为[begin,end+1]
    static int maxSubArray2(int [] nums) {
        if (nums == null || nums.length == 0) return 0;
        // 这里注意, 容易写成 int max = 0, 可能会出错, max 默认值必须是最小的值
        int max = Integer.MIN_VALUE;
        // 穷举, 列出所有可能的连续子序列, 分别计算它们的和, 最后取出最大值
        for (int begin = 0; begin < nums.length; begin++) {
            int sum = 0;
            for (int end = begin; end < nums.length; end++) {
                sum += nums[end];
            }
            if(sum > max) max = sum;
        }
        return max;
    }

解法2:分治

在这里插入图片描述

package 分治算法;

public class Main {

    public static void main(String[] args) {
        int[] nums = {-2,1,-3,4,-1,2,1,-5,4};
        System.out.println(maxSubArray(nums));
    }
    static int maxSubArray(int [] nums) {
        if (nums == null || nums.length == 0) return 0;
        // 这里注意, 容易写成 int max = 0, 可能会出错, max 默认值必须是最小的值
        int max = Integer.MIN_VALUE;
        // 穷举, 列出所有可能的连续子序列, 分别计算它们的和, 最后取出最大值
        for (int begin = 0; begin < nums.length; begin++) {
            for (int end = begin; end < nums.length; end++) {
                int sum = 0;
                for (int i = begin; i <= end ; i++) {
                    sum+= nums[i];
                }
                if(sum > max) max = sum;
            }
        }
        return max;
    }



    //todo 优化1 减去重复计算  [begin,end] 的值作为[begin,end+1]
    static int maxSubArray2(int [] nums) {
        if (nums == null || nums.length == 0) return 0;
        // 这里注意, 容易写成 int max = 0, 可能会出错, max 默认值必须是最小的值
        int max = Integer.MIN_VALUE;
        // 穷举, 列出所有可能的连续子序列, 分别计算它们的和, 最后取出最大值
        for (int begin = 0; begin < nums.length; begin++) {
            int sum = 0;
            for (int end = begin; end < nums.length; end++) {
                sum += nums[end];
            }
            if(sum > max) max = sum;
        }
        return max;
    }


    //todo 3.分治解法
    //函数功能:求出nums中begin到end之间的最大连续子序列的和
    static int maxSubArray3(int [] nums,int begin,int end) {
        if (nums == null || nums.length == 0) return 0;
        //begin到end只有一个元素的时候
        if(end - begin < 2) return nums[begin];
        int mid = (begin + end) >> 1;

        int leftMax = Integer.MIN_VALUE;
        int leftSum = Integer.MIN_VALUE;

        for(int i = mid-1;i>= begin;i--){
            leftSum += nums[i];
            leftMax = Math.max(leftMax,leftSum);
        }


        int rightMax = 0;
        int rightSum = 0;
        for(int i = mid;i < end;i++){
            rightSum  += nums[i];
            rightMax = Math.max(rightMax,rightSum);
        }

        //1.左边的最大
        //2.右边的
        //3.两边在一起的
        return  Math.max(leftMax + rightMax,
                         Math.max(maxSubArray3(nums,begin,mid),maxSubArray3(nums,mid,end)));
    }
}

大数乘法

在这里插入图片描述

	 136
*    254
----------
	  24
	 300
	1200
	 120
	1500
	6000
	 400
	5000
   20000 

在这里插入图片描述
两个n规模的数字相乘 =》 T(n)
分治:将n规模的数据相乘分解成了3个(n/2)规模的数据相乘

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值