最大子数组

                      最大连续子数组

  问题描述:一个整数数组,数组中有正数也有负数,一个或连续的多个整数组成一个子数组,求所有子数组的和的最大值。

  求解方法;暴力求解法、动态规划法、分治法(递归)

  (a)     暴力求解法就是找出所有的子数组,复杂度为O(N^2)。一般不采用。

  (b)      分治法。时间复杂度为O(nlogn)。其基本思想就是将问题分解成子问题,然后递归求解子问题,直到解决我们的问题。

  使用分治思想,那么就要将数组分成两个规模大致相等的子数组。就是说找到数组的中点,进行划分。划分之后会发现问题:最大连续子数组位于哪儿呢?(1)左子数组(2)右子数组(3)横跨两个子数组。连续子数组必然存在于三个地方的其中一个。对于(1)(2),其实就是原问题的子问题,只是规模小了,因此剩下的就是求解跨越中点的最大子数组。

  求解跨越两个子数组的最大子数组:从中点向着两端遍历,取左右两个子数组的最大值(注意:此处的最大值并不是真的最大值,因为加了限制条件,必须包含中点),然后相加即可。

  (c)      动态规划法。

  设sum[i]为以第i个元素结尾且和最大的连续子数组。假设对于元素i,所有以它前面的元素结尾的子数组的长度都已经求得,那么以第i个元素结尾且和最大的连续子数组实际上,要么是以第i-1个元素结尾且和最大的连续子数组加上这个元素,要么是只包含第i个元素,即sum[i] = max(sum[i-1]+ a[i], a[i])(状态方程)可以通过判断sum[i-1]+ a[i]是否大于a[i]来做选择,而这实际上等价于判断sum[i-1]是否大于0。由于每次运算只需要前一次的结果,因此并不需要像普通的动态规划那样保留之前所有的计算结果,只需要保留上一次的即可,因此算法的时间和空间复杂度都很小。

package sort;

import java.util.Arrays;

/*求解最大连续子数组
 * 使用分治思想:分解,求解,合并
 * */
public class LianXuZiShuZu {
	public static void main(String[] args){
		
		int[] arr = {13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7};
		int[] result = find_Max_SubArr( arr,0,arr.length-1);
		System.out.println(Arrays.toString(result));
		
		//动态规划
		Dp(arr);
	}
	
	//分治思想求解
	public static int[] find_Max_SubArr(int[] arr,int low,int high){
		if(low==high){
			int[] result = {low,high,arr[low]};
			return result;
		}
		else{
			int mid = (int)(low+high)/2;
			int[] left_result = find_Max_SubArr(arr,low,mid);//左子数组,其子问题,递归调用
			int[] right_result = find_Max_SubArr(arr,mid+1,high);//右子数组,其子问题,递归调用
			int[] cross_result = find_Cross_SubArr(arr,low,mid,high);
			
			if(left_result[2]>=right_result[2]&&left_result[2]>=cross_result[2]){
				return left_result;
			}
			else if(right_result[2]>=left_result[2]&&right_result[2]>=cross_result[2]){
				return right_result;
			}
			else{
				return cross_result;
			}
		}
	}
	
	public static int[] find_Cross_SubArr(int[] arr,int low,int mid,int high){
		int left_sum = Integer.MIN_VALUE;
		int sum =0;
		int max_left =0;
		for(int i=mid;i>=low;i--){
			sum = sum+arr[i];
			if(sum>left_sum){
				left_sum = sum;
				max_left = i;
			}
		}
		
		int right_sum = 0;
		int max_right = 0;
		sum = 0;
		for(int j=mid+1;j<=high;j++){
			sum = sum+arr[j];
			if(sum>right_sum){
				right_sum = sum;
				max_right = j;
			}
		}
		
		int[] result = {max_left,max_right,left_sum+right_sum};
		return result;
	}
	
	
	//动态规划思想求解 基本思想就是:sum[i] = max(sum[i-1] + a[i], a[i])  
	public static void Dp(int[] arr){
		int curSum = 0;
		int sum = 0;
		for(int i=0;i<arr.length;i++){
			if(curSum<=0){
				curSum = arr[i];
			}
			else{
				curSum = curSum+arr[i];
			}
			if(curSum>sum){
				sum = curSum;
			}
		}
		System.out.println(sum);
	}

}























### 最大子数组算法的实现 最大子数组问题的目标是找到一个数组中最大的连续子数组。该问题在算法领域中具有重要意义,尤其在动态规划分治算法的应用中被广泛研究。 #### 方法1:蛮力法 蛮力法是最直观的解决方案,其基本思想是遍历所有可能的子数组,计算它们的,并从中找出最大值。这种方法的时间复杂度为 $O(n^2)$,对于较小的数组规模是可行的,但对于较大的数组则效率较低。 以下是C语言实现: ```c #include <stdio.h> int maxSubArray1(int a[], int n) { int maxSum = 0; int i, j; for (i = 0; i < n; i++) { int currentSum = 0; for (j = i; j < n; j++) { currentSum += a[j]; if (currentSum > maxSum) { maxSum = currentSum; } } } return maxSum; } int main() { int arr[] = {-2, 1, -3, 4, -1, 2, 1, -5, 4}; int n = sizeof(arr) / sizeof(arr[0]); printf("最大子数组为: %d\n", maxSubArray1(arr, n)); return 0; } ``` #### 方法2:动态规划法 动态规划法是解决最大子数组问题的经典方法,其时间复杂度为 $O(n)$,效率显著优于蛮力法。其核心思想是维护一个当前子数组,如果当前子数组小于0,则丢弃之前的子数组并重新开始计算。 以下是C语言实现: ```c #include <stdio.h> int maxSubArray2(int a[], int n) { int maxSum = a[0]; int currentSum = a[0]; for (int i = 1; i < n; i++) { currentSum = (currentSum + a[i] > a[i]) ? currentSum + a[i] : a[i]; maxSum = (currentSum > maxSum) ? currentSum : maxSum; } return maxSum; } int main() { int arr[] = {-2, 1, -3, 4, -1, 2, 1, -5, 4}; int n = sizeof(arr) / sizeof(arr[0]); printf("最大子数组为: %d\n", maxSubArray2(arr, n)); return 0; } ``` #### 方法3:分治法 分治法将数组分为左右两部分,分别求解左半部分右半部分的最大子数组,同时考虑跨越中间的最大子数组。这种方法的时间复杂度为 $O(n \log n)$,适合大规模数据的处理。 以下是C语言实现: ```c #include <stdio.h> // 求三者中的最大值 int max(int a, int b, int c) { return (a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c); } // 求跨越中间的最大子数组 int maxCrossingSum(int a[], int left, int mid, int right) { int sum = 0; int leftSum = -999999; for (int i = mid; i >= left; i--) { sum += a[i]; if (sum > leftSum) { leftSum = sum; } } sum = 0; int rightSum = -999999; for (int i = mid + 1; i <= right; i++) { sum += a[i]; if (sum > rightSum) { rightSum = sum; } } return leftSum + rightSum; } // 分治法主函数 int maxSubArray3(int a[], int left, int right) { if (left == right) { return a[left]; } int mid = (left + right) / 2; int leftMax = maxSubArray3(a, left, mid); int rightMax = maxSubArray3(a, mid + 1, right); int crossMax = maxCrossingSum(a, left, mid, right); return max(leftMax, rightMax, crossMax); } int main() { int arr[] = {-2, 1, -3, 4, -1, 2, 1, -5, 4}; int n = sizeof(arr) / sizeof(arr[0]); printf("最大子数组为: %d\n", maxSubArray3(arr, 0, n - 1)); return 0; } ``` ### 总结 - **蛮力法**:简单直观,但效率较低,时间复杂度为 $O(n^2)$。 - **动态规划法**:高效且简洁,时间复杂度为 $O(n)$。 - **分治法**:适用于大规模数据,时间复杂度为 $O(n \log n)$,但实现较复杂。 根据具体需求数据规模,可以选择合适的算法实现最大子数组的求解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值