面试题 16.17. 连续数列
题目来源:力扣(LeetCode)
https://leetcode-cn.com/problems/contiguous-sequence-lcci/
解题思路:
为了使用分治算法而使用分治算法求解:
首先考虑如何将一个原问题如何分解为一系列子问题。简单想一下,如果数组中只有一个元素,那最大值直接就求出来了,于是想着将原数组一直分解下去,直到最后只有一个元素。一个数组中元素和最大的连续序列可能是存在数组的左边、也可能是右边,还有一种可能是中间,也只有这三种可能。
- 我们从中间位置将数组分成两个子数组,这样就将原问题分解为两个子问题;
- 一直分解下去,直到子数组只有一个元素,该子数组的解就一目了然了;
- 如何合并子问题的解:原问题分解为两个子问题后,如果求得这两个子问题的解,这两个子问题的解是相互独立的,可以通过选择这两个解的最大值作为原数组可能的最优解;通过子问题的解,我们得到两情况的解,但还有一种中间区域的解还没求解,这个解是不能通过子问题得到的,需要单独计算得到,最终选择三者中的最大值。
分治算法代码:
public int maxSubArray(int[] nums) {
int left = 0;
int right = nums.length; // 左闭右开
return recursive(nums,left,right);
}
private int recursive(int[] nums, int left, int right){
if(right-left<2) return nums[left]; // 区间只有一个元素时,直接返回
int middle = (right+left) >> 1; // 中间位置归属右区间
// 左边最大值
int leftMax = recursive(nums, left, middle);
// 右边最大值
int rightMax = recursive(nums, middle, right);
// 中间最大值求解
int middleMax;
// 中间元素连续左边最大初始值,累加初始值为0。
int leftSum = 0, leftBorderMax = nums[middle-1];
// 中间元素连续右边最大初始值,累加初始值为0。
int rightSum = 0, rightBorderMax = nums[middle];
// 从中间元素左边第一个元素求连续左边最大值
for(int i=middle-1; i>=left; i--){
leftSum += nums[i];
leftBorderMax = leftBorderMax>leftSum ? leftBorderMax : leftSum;
}
// 从中间元素开始求连续右边最大值
for(int i=middle; i<right; i++){
rightSum += nums[i];
rightBorderMax = rightBorderMax>rightSum ? rightBorderMax : rightSum;
}
// 中间元素连续左右边最大值相加即为中间区间最大值。
middleMax = leftBorderMax + rightBorderMax;
int max;
max = leftMax > rightMax ? leftMax : rightMax;
max = max > middleMax ? max : middleMax;
return max; // 取三者中的最大值。
}
这题还有更简单的方式,一个循环即可。
public int maxSubArray(int[] nums) {
// 假设第一个元素最大
int max = nums[0];
int sum = 0;
for(int i=0; i<nums.length;i++){
sum += nums[i];
max = (max>sum) ? max : sum;
if(sum<0) sum = 0;
}
return max;
}
使用递归代替循环:
private int max;
private int sum;
public int maxSubArray(int[] nums) {
// 假设第一个元素最大
max = nums[0];
sum = 0;
recursive(nums,0);
return max;
}
private void recursive(int[] nums,int i){
if(i>nums.length-1) return; // 递归退出条件
sum = sum + nums[i];
max = (max>sum) ? max : sum;
if(sum<0) sum = 0;
recursive(nums,++i);
}