分治法求解最大子数组

问题描述: 给定文件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 
#include 

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,运行结果如下图所示:
分治法求解最大子数组_第1张图片

你可能感兴趣的:(算法,算法,分治法,C语言)