《算法导论》中讲分治策略的第一道例题就是一道"最大子数组问题",抽象出数学模型,题意是想求出数组中的连续子数组和的最大值。
很明显,暴力求解不加任何优化是O(n^2)的时间复杂度(暴力枚举左和右的组合,然后进行求和),在大数据范围时会有超时的风险,所以我们要进行算法的优化。
使用分治策略的求解方法
假设我们要寻找的子数组A[low, high]的最大子数组。使用分治技术意味着我们要将子数组划分为两个规模尽量相等的子数组。也就是说,找到子数组的中央位置mid,然后考虑求解A[low, mid]和A[mid+1, high]两个子数组。
A[low, high]的最大子数组必然是以下三种情况之一:
1.完全位于A[low, mid]当中
2.完全位于A[mid+1, high]当中
3.跨越了中点
也就是分别求出他们三种情况的最大值后找出三种情况的最大值就是A[low, high]的最大子数组。由于前两种情况可以简单进行递归求解,所以我们只考虑第三种情况的计算。
第三种情况单独列出来也并不难求,只要分别得出以mid为终点的最大子数组和以mid+1为起点的最大子数组,就可以在O(n)的时间复杂度求出满足条件的最大值。
接下来列出书中的伪代码:
FIND-MAX-CROSSING-SUBARRAY(A, low, mid, high)
left-sum = -∞
sum = 0
for i = mid downto low
sum = sum + A[i]
if sum > left-sum
left-sum = sum
max-left = i
right-sum = -∞
sum = 0
for j = mid + 1 to high
sum = sum + A[i]
if sum > right-sum
right-sum = sum
max-right = j
return (max-left, max-right, left-sum + right-sum)
FIND-MAXIMUM-SUBARRAY(A, low, high)
if high == low
return (low, high, A[low])
else mid = ⌊(low+high)/2⌋
(left-low, left-hight, left-sum) =
FIND-MAXIMUM-SUBARRAY(A, low, mid)
(right-low, right-hight, right-sum) =
FIND-MAXIMUM-SUBARRAY(A, mid + 1, high)
(cross-low, cross-high, cross-sum) =
FIND-MAX-CROSSING-SUBARRAY(A, low, mid, high)
if left-sum >= right-sum and left-sum >= cross-sum
return (left-low, left-high, left-sum)
elseif right-sum >= left-sum and right-sum >= cross-sum
return (right-low, right-high, right-sum)
else return (cross-low, cross-high, cross-sum)
将伪代码改成能直接表示出思路的注释:
typedef struct triad{
int l, r, sum;
}triad;
triad FindMaxCrossingSubarray(int *A, int low, int mid, int high){
//定义变量记录左半部分到mid的和的最大值并初始化
//定义变量记录当前位置到mid的和并初始化为0
//从mid到low依次相加,中间记录和的最大值以及对应的坐标
//定义变量记录mid+1到右半部分的和的最大值并初始化
//定义变量记录mid+1到当付钱位置的和并初始化为0
//从mid到high依次相加,中间记录和的最大值以及对应的坐标
//返回左半部分记录的坐标,右半部分记录的坐标和两部分的求和
}
triad FindMaximumSubarray(int *A, int low, int high){
//当左右指针相遇,说明已经触底,可以直接返回左右坐标以及剩余数的值
//当左右指针没有相遇时,说明还可以继续向下递归
//找到当前子数组的中值mid
//递归求A[low, mid]的最大子数组和
//递归求A[mid+1, high]的最大子数组和
//调用FindMaxCrossingSunarray函数求出通过中点的子数组和的最大值
//返回三种情况中的最大值
}
接下来我们将注释对应的代码填入来完成函数:
typedef struct triad{
/*
l:左值
r:右值
sum:求和
*/
int l, r, sum;
}triad;
triad FindMaxCrossingSubarray(int *A, int low, int mid, int high){
/*
A:数组
low:左端点
mid:中间点
high:右端点
*/
int leftSum = -1e9;
int sum = 0;
int maxLeft, maxRight;
for(int i = mid-1; i >= low; i--){
sum += A[i];
if(sum > leftSum){
leftSum = sum;
maxLeft = i;
}
}
int rightSum = -1e9;
sum = 0;
for(int i = mid; i < high; i++){
sum += A[i];
if(sum > rightSum){
rightSum = sum;
maxRight = i;
}
}
triad res;
res.l = maxLeft;
res.r = maxRight;
res.sum = leftSum+rightSum;
return res;
}
triad FindMaximumSubarray(int *A, int low, int high){
/*
A:数组
low:左端点
high:右端点
*/
triad res;
if(high - 1 == low){
res.l = low;
res.r = high;
res.sum = A[low];
return res;
}
else{
int mid = (low + high) / 2;
triad resLeft, resRight, resCross;
resLeft = FindMaximumSubarray(A, low, mid);
resRight = FindMaximumSubarray(A, mid, high);
resCross = FindMaxCrossingSubarray(A, low, mid, high);
if(resLeft.sum >= resRight.sum && resLeft.sum >= resCross.sum){
return resLeft;
}
else if(resRight.sum >= resLeft.sum && resRight.sum >= resCross.sum){
return resRight;
}
else{
return resCross;
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现