算法导论之暴力破解法与分治法解决最大子数组问题分析
算法导论之暴力破解法与分治法解决最大子数组问题分析
思想
分治法的主要思想:分解,解决,合并,即将一个问题分解成多个子问题,分别求解每一个子问题,再将每一个子问题进行合并。分治法的时间复杂度为每个子问题的复杂度之和。
个人理解分治法是解决问题的一种思路,不一定在所有问题中分治法就比暴力法的效率高。具体要视问题而定。
最大子数组的和问题
最大子数组和的问题,算是一道比较经典的算法问题。一个任意整数的数组,如何求解该数组的子数组的所有元素相加的最大和。
暴力法
暴力法的求解比较简单,设置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);
}
}
下面计算分治法的时间复杂度,这块的推导还没看到,等看完了再进行补充