分治法求解最大子数组

问题描述: 给定文件data.txt(该文件可在本文所附资源处或点击此处链接下载),data.txt的每一行是一个数字,在这些数字按行序构成的数组中找到一个子数组,使得子数组中的元素和最大。

1. 思路

  1. 分治法将整个数组分成两半,那么它的最大子数组有三种可能的情况,
    ① 完全位于左半边;
    ② 完全位于右半边;
    ③ 跨越左半边和右半边。
    取这三种情况下子数组元素和的最大值,就可以得到整个数组的最大子数组了。
  2. 当原始数组只有1个元素时,最大子数组就是它本身。

因此,首先我定义了一个全局数组a[666666],由于data.txt中有666665个数字,设置数组长度为666666可以存下。同时,我定义了一个三元组结构Triple,用于存储最大子数组的左边界下标、右边界下标和元素之和。

然后,实现函数maxCrossingSubArray(a, low, mid, high)返回跨越数组a中点的最大子数组,返回格式为三元组Triple

最后,实现函数maxSumSubArray(a, low, high)返回数组a的最大子数组,采用分治法在情况①和情况②下递归调用函数maxSumSubArray,在情况③下调用函数maxCrossingSubArray

2. 代码

#include <stdio.h>
#include <stdlib.h>

int a[666666];  //定义全局数组,储存测试数据 

struct Triple { //三元组,储存最大子数组的左边界、右边界及求和 
	int low;    //最大子数组的左边界下标 
	int high;   //最大子数组的右边界下标 
	int sum;    //最大子数组各元素之和 
}Triple;

//返回跨越数组中点的最大子数组 
struct Triple maxCrossingSubArray(int a[], int low, int mid, int high) {
	//求a[low..mid]的以a[mid]为右侧边界的最大子数组 
	int maxLeft;              //记录最大子数组左侧边界的下标 
	int leftSum = -10000;     //记录a[low..mid]中找到的最大和 
	int sum = 0;              //记录a[low..mid]中所有值的和 
	int i;
	for (i = mid; i >= low; i--) {
		sum += a[i];
		if (sum > leftSum) {
			leftSum = sum;
			maxLeft = i;
		}
	}
	
	//求a[mid+1..high]的以a[mid+1]为左侧边界的最大子数组 
	int maxRight;              //记录最大子数组右侧边界的下标 
	int rightSum = -10000;     //记录a[mid+1..high]中找到的最大和 
	sum = 0;                   //记录a[mid+1..high]中所有值的和 
	for (i = mid+1; i <= high; i++) {
		sum += a[i];
		if (sum > rightSum) {
			rightSum = sum;
			maxRight = i;
		}
	}
	
	//以三元组形式储存找到的最大子数组并返回 
	struct Triple t;
	t.low = maxLeft;
	t.high = maxRight;
	t.sum = leftSum + rightSum;
	return t;
}

//返回a[]的最大子数组 
struct Triple maxSumSubArray(int a[], int low, int high) {
	if (high == low) {      //数组a[]中只有一个元素
		struct Triple t;
		t.low = low;
		t.high = high;
		t.sum = a[low];
		return t;
	} 
	else {
		int mid = (low + high) / 2;
		struct Triple left = maxSumSubArray(a, low, mid);//寻找完全位于a[low..mid]的最大子数组 
		struct Triple right = maxSumSubArray(a, mid+1, high);//寻找完全位于a[mid+1..high]的最大子数组 
		struct Triple cross = maxCrossingSubArray(a, low, mid, high);//寻找跨越终点的最大子数组 
		if (left.sum >= right.sum && left.sum >= cross.sum) {
			return left; 
		}
		else if (right.sum >= left.sum && right.sum >= cross.sum) {
			return right; 
		}
		else {
			return cross;
		}
	}
}

int main() {	
	FILE *fp = fopen("data.txt","r");            //打开文件 
	if (fp == NULL) {
		printf("Can not open the file!\n");
		exit(0);
	}
	int i = 0;
	while (fscanf(fp, "%d\n", &a[i]) != EOF) {  //读取文件中的数据到数组a[]中 
		i++;
	}
	fclose(fp);                                 //关闭文件 
	struct Triple result = maxSumSubArray(a, 0, i-1);
	printf("low: %d\nhigh: %d\nsum: %d\n", result.low, result.high, result.sum);
	return 0;
} 

3. 运行结果

基于测试数据data.txt,运行结果如下图所示:
运行结果

  • 12
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是使用分治法求解最大连续数组(字段和)问题的Java代码: ```java public class MaximumSubarray { public static int[] findMaximumSubarray(int[] nums) { return findMaximumSubarray(nums, 0, nums.length - 1); } private static int[] findMaximumCrossingSubarray(int[] nums, int low, int mid, int high) { int leftSum = Integer.MIN_VALUE; int sum = 0; int maxLeft = -1; for (int i = mid; i >= low; i--) { sum += nums[i]; if (sum > leftSum) { leftSum = sum; maxLeft = i; } } int rightSum = Integer.MIN_VALUE; sum = 0; int maxRight = -1; for (int j = mid + 1; j <= high; j++) { sum += nums[j]; if (sum > rightSum) { rightSum = sum; maxRight = j; } } return new int[]{maxLeft, maxRight, leftSum + rightSum}; } private static int[] findMaximumSubarray(int[] nums, int low, int high) { if (low == high) { return new int[]{low, high, nums[low]}; } else { int mid = (low + high) / 2; int[] left = findMaximumSubarray(nums, low, mid); int[] right = findMaximumSubarray(nums, mid + 1, high); int[] cross = findMaximumCrossingSubarray(nums, low, mid, high); if (left[2] >= right[2] && left[2] >= cross[2]) { return left; } else if (right[2] >= left[2] && right[2] >= cross[2]) { return right; } else { return cross; } } } public static void main(String[] args) { int[] nums = {1, -2, 3, 4, -5, 6, 7, -8, 9}; int[] result = findMaximumSubarray(nums); System.out.println("Maximum subarray sum: " + result[2]); System.out.println("Maximum subarray indices: [" + result[0] + ", " + result[1] + "]"); } } ``` 在这个示例中,我们定义了一个 `findMaximumSubarray()` 方法来找到给定数组最大连续数组。我们使用分治法来解决这个问题。我们首先将数组划分为两个数组,然后分别找到左半部分、右半部分和跨越中心的最大数组。最后,我们比较这三个结果,返回具有最大和的数组。 我们还定义了一个 `findMaximumCrossingSubarray()` 方法来找到跨越中心的最大数组。在这个方法中,我们首先找到左半部分的最大数组,然后找到右半部分的最大数组。通过将这两个数组组合起来,我们可以找到跨越中心的最大数组。 在 `main()` 方法中,我们创建了一个示例数组,并使用 `findMaximumSubarray()` 方法来找到最大数组的和和索引。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

fufufunny

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值