小白进阶之寻找最大子数组问题

本文介绍了寻找最大子数组和问题的三种解决方案:暴力求解法、分治法和动态规划法,并提供了每种方法的详细解释及源代码实现。

寻找最大子数组问题即为寻求数组中连续的最大子数组和。我尝试了三种求解办法:分治法、暴力求解法和动态规划。个人觉得分治法实现起来较为困难,而暴力求解法虽然时间复杂度较大,但是实现起来简单呐,然后动态规划实在是让人心旷神怡的解决办法,强烈推荐。

暴力求解法:

暴力求解法就是算出所有可能的子数组和,比较大小,得出最大子数组。

下面时暴力求解法源码:

int findMaxSubarray__(int a[],int n,int& left,int& right){
	int i,j;
	int sum,maxsum=INT_MIN;
	for(i=0;i<n;i++){
		sum=0;
		for(j=i;j<n;j++){
			sum+=a[j];
			if(sum>maxsum){
				maxsum=sum;
				left=i;
				right=j;
			}
		}
	}
	return maxsum;
}
分治法:

分治法是把最大子数组和问题分解为:求左边最大子数组,求右边最大子数组和求跨越中间的最大子数组。其中,求左边最大子数组和求右边最大子数组都是用递归求解,而中间最大子数组则是从中间开始,分别向左边和右边求最大和,之后加到一起即可。最后,比较左边最大子数组和、右边最大子数组和、中间最大子数组和,取最大即可。

下面是分治法源码:

int findMaxSubarray_(int a[],int l,int r,int& left,int& right){
	if(l<r){
		int mid=(l+r)/2;
		int ll,lr;
		int suml=findMaxSubarray_(a,l,mid,ll,lr);
		int rl,rr;
		int sumr=findMaxSubarray_(a,mid+1,r,rl,rr);
		int sumc=0;
		int maxLeft=INT_MIN;
		int ml,mr;
		int i;
		for(i=mid;i>=l;i--){
			sumc+=a[i];
			if(sumc>maxLeft){
				maxLeft=sumc;
				ml=i;
			}
		}
		int maxRight=INT_MIN;
		sumc=0;
		for(i=mid+1;i<=r;i++){
			sumc+=a[i];
			if(sumc>maxRight){
				maxRight=sumc;
				mr=i;
			}
		}
		sumc=maxLeft+maxRight;
		if(suml<sumr){
			if(sumr<sumc){
				left=ml;
				right=mr;
				return sumc;
			}
			else{
				left=rl;
				right=rr;
				return sumr;
			}
		}
		else{
			if(suml<sumc){
				left=ml;
				right=mr;
				return sumc;
			}
			else{
				left=ll;
				right=lr;
				return suml;
			}
		}
	}
	else{
		left=l;
		right=r;
		return a[l];
	}
}

动态规划:

动态规划是用maxsum表示最大子数组,然后从0到n-1进行累加,如果前值小于0,则前值置0之后再进行累加,否则就直接累加。每次累加都和maxsum比较大小,始终保持maxsum是两者中的最大值。

下面是动态规划法源码:

int findMaxSubarray___(int a[],int n,int& left,int& right){
	int cursum=a[0];
	int maxsum=a[0];
	int i;
	left=0;
	right=0;
	for(i=1;i<n;i++){
		if(cursum<0){
			cursum=0;
			left=i;
			right=i;
		}
		cursum+=a[i];
		if(cursum>maxsum){
			maxsum=cursum;
			right=i;
		}
	}
	return maxsum;
}

这次最大的收获是认识了int &这个类型,其中&表示引用,可以进行值的传递,用起来非常方便。下面的链接详述了int &的用法及它和int、int *的区别。
http://blog.youkuaiyun.com/willian0621/article/details/12838157

最大子数组是连续的若干数组元素,若其和是最大的,那么这个子数组就称为该数组的最大子数组,它是很多问题的抽象,比如购买股票,把相邻两天的股价之差作为数组元素,求在连续的某个时间段内买入股票的最佳时间和卖出股票的最佳时间就可以抽象为计算最大子数组问题 [^3]。 求解最大子数组问题分治法和贪心算法(这里结合动态规划思想的变种贪心算法,即Kadane算法)等方法: - **分治法**:将数组 `X[1…n]` 分为 `X[1…n/2]` 和 `X[n/2 +1…n]`,递归求解子问题,包括数组 `X[1…n/2]` 的最大子数组 `S1`、数组 `X[n/2+1…n]` 的最大子数组 `S2`,然后计算跨中点的最大子数组 `S3`,数组 `X` 的最大子数组之和 `Smax = max{S1, S2, S3}`,时间复杂度为 $O(nlogn)$ [^5]。 ```python def find_max_crossing_subarray(arr, low, mid, high): left_sum = float('-inf') sum_val = 0 max_left = mid for i in range(mid, low - 1, -1): sum_val += arr[i] if sum_val > left_sum: left_sum = sum_val max_left = i right_sum = float('-inf') sum_val = 0 max_right = mid + 1 for j in range(mid + 1, high + 1): sum_val += arr[j] if sum_val > right_sum: right_sum = sum_val max_right = j return max_left, max_right, left_sum + right_sum def find_max_subarray(arr, low, high): if high == low: return low, high, arr[low] else: mid = (low + high) // 2 left_low, left_high, left_sum = find_max_subarray(arr, low, mid) right_low, right_high, right_sum = find_max_subarray(arr, mid + 1, high) cross_low, cross_high, cross_sum = find_max_crossing_subarray(arr, low, mid, high) if left_sum >= right_sum and left_sum >= cross_sum: return left_low, left_high, left_sum elif right_sum >= left_sum and right_sum >= cross_sum: return right_low, right_high, right_sum else: return cross_low, cross_high, cross_sum arr = [13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7] low, high, max_sum = find_max_subarray(arr, 0, len(arr) - 1) print(f"最大子数组的起始下标: {low}, 结束下标: {high}, 最大和: {max_sum}") ``` - **Kadane算法**:从数组左边界开始,由左到右处理,记录到目前为止已处理过的最大子数组。若已知 `A[1...j]` 的最大子数组,`A[1...j+1]` 的最大子数组要么是 `A[1...j]` 的最大子数组,要么是某个子数组 `A[i...j+1]` 的最大子数组(`1 <= i <= j+1`),在已知 `A[1...j]` 的最大子数组的情况下,可以在线性时间内找出形如 `A[i...j+1]` 的最大子数组,时间复杂度为 $O(n)$ [^1]。 ```python def max_subarray_sum(arr): max_ending_here = max_so_far = arr[0] for num in arr[1:]: max_ending_here = max(num, max_ending_here + num) max_so_far = max(max_so_far, max_ending_here) return max_so_far arr = [13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7] print(f"最大子数组和: {max_subarray_sum(arr)}") ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值