算法导论之暴力破解法与分治法解决最大子数组问题分析

算法导论之暴力破解法与分治法解决最大子数组问题分析

思想

分治法的主要思想:分解,解决,合并,即将一个问题分解成多个子问题,分别求解每一个子问题,再将每一个子问题进行合并。分治法的时间复杂度为每个子问题的复杂度之和。
个人理解分治法是解决问题的一种思路,不一定在所有问题中分治法就比暴力法的效率高。具体要视问题而定。

最大子数组的和问题

最大子数组和的问题,算是一道比较经典的算法问题。一个任意整数的数组,如何求解该数组的子数组的所有元素相加的最大和。

暴力法

暴力法的求解比较简单,设置2个数组游标i和j,分别遍历i和j即可求出最大子数组的和,下面是伪代码

int low,hight,sum = 0;
int maxsum = -∞
int array[];
for (int i = 0; i < array.size(), ++i) {
	sum = 0;
	for (int j = i; j < array.size(); ++j) {
		sum += array[j];
		if (maxsum < sum) {
			maxsum = sum;
			low = i;
			hight = j;
		}
	}
}

从上面的代码我们可以分析
第1~3行执行时间为常量时间,即θ(1)
第4行会执行n + 1次,执行时间为常量时间,n + 1 = θ(n)
第5行同理也为θ(n);
第6~11行会执行n *(n + (n - 1) + (n - 2) + ... + 2 + 1)次,即为n^2 +(n - 1)^2 + ... + 2n + n = θ(n^2)

所以整段程序的渐进复杂度为θ(1) + θ(n) + θ(n) + θ(n^2) = θ(n^2),随着n--->∞, θ(n) /θ(n^2) = 0,所以上述表达式的渐进复杂度为θ(n^2)。存在θ(n^2),即存在O(n^2)

分治法

开篇介绍了分治法的主要思想:分解,解决,合并
使用分治法求解子数组和的问题,我们首先要分解问题,求出数组的中点位置,即将1个数组分解成了2个二分之一数组。
array[low…mid]和array[mid + 1…hight],简称a1和a2,最大子数组的和的组成有3种可能

  • low和hight都在a1上;
  • low和hight都在a2上;
  • low和hight分别在a1和a2上。

在a1和a2两个数组上又可以进行分解,分解成a1’,a2’和a1’’,a2’’。。。。。。。。
直到数组的长度为1,则最大子数组的和就是它本身。然后再根据上面3种可能判断最大子数组的分布是哪种情况,一层一层合并后即可获得解答。

第三种情况的伪代码如下:

function3(array, low, mid, hight)
left_sum = -∞; // 左子数组的最大和
right_sum = -∞; // 右子数组的最大和
left_index = 0; // 左子数组最大和的索引
right_index = 0; // 右子数组最大和的索引
sum = 0;

// 获取从mid开始的左子数组的最大和
for (int i = mid, i >= low; --i) {                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
	sum += array[i];
	if (sum > left_sum) {
		left_sum = sum;
		left_index = i;
	}

sum = 0;

// 获取从mid + 1开始的右子数组的最大和
for (int i = mid + 1; i <= hight; ++i) {
	sum += array[i];
	if (sum > right_sum) {
	right_sum = sum;
	right_index = i;
}

// 返回左右子数组的最大和
return (left_index, right_index, left_sum + right_sum);

再结合第一第二种情况,可得下列伪代码:

function(array, low, hight) {
	if (hight == low) {
		return (low, hight, array[low]);
	}
	mid = (low + hight) / 2;

	// 第一种情况,最大子数组和出现在左子数组
	(left_low, left_hight, left_sum) = function(array, low, mid);

	// 第二种情况,最大子数组和出现在右子数组
	(right_low, right_hight, right_sum) = function(array, mid + 1, hight);

    // 第三种情况,最大子数组和分布在左右子数组之间
	(cross_low, cross_hight, cross_sum) = function3(array, low, mid, hight);

	// 三种情况对比,返回最大值
	if (left_sum >= right_sum && left_sum >= cross_sum) {
		return (left_low, left_hight, left_sum);
	} else if (right_sum >= left_sum && right_sum >= cross_sum){
		return (right_low, right_hight, right_sum);
	} else {
		return (cross_low, cross_hight, cross_sum);
	}
}

下面计算分治法的时间复杂度,这块的推导还没看到,等看完了再进行补充

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
算法伪代码: ``` // 求解最大数组问题 // 输入:数组 nums,数组起始下标 low,数组结束下标 high // 输出:数组 nums 在下标 low 到 high 之间最大数组的和 function maxSubArray(nums, low, high): // 递归结束条件 if low == high: return nums[low] // 分治求解 mid = (low + high) / 2 leftMaxSum = maxSubArray(nums, low, mid) // 左半部分最大数组和 rightMaxSum = maxSubArray(nums, mid+1, high) // 右半部分最大数组和 crossMaxSum = maxCrossSubArray(nums, low, mid, high) // 跨越中点的最大数组和 // 返回三者中最大的值 return max(leftMaxSum, rightMaxSum, crossMaxSum) // 求解跨越中点的最大数组和 // 输入:数组 nums,数组起始下标 low,数组中点下标 mid,数组结束下标 high // 输出:数组 nums 在下标 low 到 high 之间跨越中点的最大数组的和 function maxCrossSubArray(nums, low, mid, high): // 计算包含中点的左半部分最大数组和 leftMaxSum = -INF sum = 0 for i = mid downto low: sum = sum + nums[i] leftMaxSum = max(leftMaxSum, sum) // 计算包含中点的右半部分最大数组和 rightMaxSum = -INF sum = 0 for i = mid+1 to high: sum = sum + nums[i] rightMaxSum = max(rightMaxSum, sum) // 返回左右两部分加起来的和 return leftMaxSum + rightMaxSum ``` 算法时间复杂度分析: 该算法的时间复杂度为 $O(nlogn)$,其中 $n$ 为数组的长度。因为该算法是一个分治算法,每次递归都将问题规模减半,因此递归层数为 $logn$。在每层递归中,需要线性时间 $O(n)$ 计算跨越中点的最大数组和,因此总时间复杂度为 $O(nlogn)$。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值