分治法解决最大子数组问题

Posted 算法小筑

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了分治法解决最大子数组问题相关的知识,希望对你有一定的参考价值。


咱们在回忆下分治策略:

  • 分解(Divide)步骤:将问题划分为一些子问题,子问题的形式与原问题一样,只是规模更小

  • 解决(Conquer)步骤:递归地求解出子问题。如果子问题的规模足够小,则停止递归,直接求解

  • 合并(Combine)步骤:将子问题的解组合成原问题的解

最大子数组问题:

有这么一个数组: 

[13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7]

数组中要包含负数, 要不没有意义. 全是正数的数组求和即可.

求出数组中连续和最大的子数组,称为最大子数组.

用分治法进行分析:

要找子数组A[low..high]的最大子数组,使用分治法意味着第一步要将数组划分为两个规模尽可能相等的子数组。即找到数组的中央位置,比如mid,第二步求解两个子数组A[low..mid]和A[mid + 1..high]。所以,A[low..high]的任何连续子数组A[i..j]所处的位置必然是三种情况之一:

1.完全位于子数组A[low..mid]中, 因此low≤ i ≤j ≤ mid;

2.完全位于子数组A[mid + 1..high]中,因此mid<=i<=j<=high;

3.跨越了中点,因此low<=i<=mid<j<=high;

知道了原理下面看代码Python版本

# coding=utf-8

def Find_Max_SubArray(A, low, high): """ 找到最大子数组 """ if high == low: # 这里是递归返回, 一步一步递归最后只剩一个就是最大的 return [low, high, A[low - 1]] else: mid = (low + high) // 2 left = Find_Max_SubArray(A, low, mid) right = Find_Max_SubArray(A, mid+1, high) cross = Find_Max_Cross_SubArray(A, low, mid, high) if left[2] >= right[2] and left[2] >= cross[2]: return left elif right[2] >= left[2] and right[2] >= cross[2]: return right else: return cross

def Find_Max_Cross_SubArray(A, low, mid, high): """ 最大子数组范围在low<=i<=mid<=j<=high """ left_sum = float('-inf') sum = 0 max_left = 0 for i in range(mid, low, -1): sum = sum + int(A[i]) if sum > left_sum: left_sum = sum            max_left = i right_sum = float('-inf') sum = 0 max_right = 0 for j in range(mid + 1, high): sum = sum + A[j] if sum > right_sum: right_sum = sum max_right = j return [max_left, max_right, (left_sum + right_sum)]

if __name__ == '__main__': import random randomlist = random.sample(range(-20, 20), 16) low = 0 high = len(randomlist) print("数组为: ", randomlist) print("最大子数组为: ", Find_Max_SubArray(randomlist, low, high))

C语言版本:

#include<stdio.h>#include <stdlib.h>#include <time.h>#define N 16
typedef struct Array_Message{ int low; int high; int sum;} array_info;
array_info* Max_Cross_Array(int data[], int low, int mid, int high){ int left_sum = INT_MIN; int max_left = 0; int sum = 0; for (int i = mid; i >= low; --i) { sum += data[i]; if (sum > left_sum) { left_sum = sum; } max_left = i; } int right_sum = INT_MIN; int max_right = 0; sum = 0; for (int i = mid + 1; i <= high; ++i) { sum += data[i]; if (sum > right_sum) { right_sum = sum; } max_right = i; } array_info* answer = (array_info*)malloc(sizeof(array_info)); if (answer == NULL) { printf("Out of space!"); } else { answer->low = max_left; answer->high = max_right; answer->sum = left_sum + right_sum; } return answer;}

array_info* Max_Child_Array(int data[], int low, int high){ int mid; if (high == low) { array_info* Max_Array = (array_info*)malloc(sizeof(array_info)); if (Max_Array == NULL) { printf("Out of space!"); } else { Max_Array->low = low; Max_Array->high = high; Max_Array->sum = data[low]; } return Max_Array; } else mid = (high + low) / 2; array_info* left_array = Max_Child_Array(data, low, mid); array_info* right_array = Max_Child_Array(data, mid + 1, high); array_info* mid_array = Max_Cross_Array(data, low, mid, high); if (left_array->sum >= mid_array->sum && left_array->sum >= right_array->sum) { return left_array; } else if (right_array->sum >= mid_array->sum && right_array->sum >= left_array->sum) { return right_array; } else return mid_array;}
int* random_list(){ static int a[N], aa; int i; srand((unsigned)time(NULL));
for (i = 0; i < N - 1; i++) { aa = rand() % 20 - 8; a[i] = aa; } return a;}

int func_free(array_info** p){ free(*p); p = NULL; return 0;}
int main(){ int* num; num = random_list(); array_info* MaxArray = Max_Child_Array(num, 0, N - 1); printf("[%d, %d, %d]", MaxArray->low, MaxArray->high, MaxArray->sum); func_free(MaxArray); return 0;}

嵌套malloc写的不是很好,可能会引起内存泄漏.

还有一种是动态规划的方法求解最大子数组问题,时间可以控制在线性时间.咱们聊到动态规划的时候再说.


如果喜欢本文请关注:

以上是关于分治法解决最大子数组问题的主要内容,如果未能解决你的问题,请参考以下文章

分治法求解最大子段和问题

分治法 解决最大字段和问题

分治算法解最大子序列和问题

分治法

分治法求最大子序列

53-最大子序和(动态规划与分治法)