《算法导论》-分治策略-最大子数组问题,理解与实现(Java,C++)

    关于最大子数组问题如果采用暴力破解法将会很容易实现,但是暴力破解复杂度可想而知,所有如果采用分治策略可以大大减少运行时间,下面将分析分治策略如何解决这个问题,通过伪代码来表示执行过程,并用Java,C++进行实现.

一.分析

   先提出一个数组:

 如果将一个数组划分成两个,那么最大子数组出现的位置有三个:

1.全在num[low,mid]也就是左边

2.全在nums[mid+1,high]也就是右边

3.穿过mid,一部分在左边一部分在右边,也就是案例数组的情况,用数组表示就是:

nums[low<=i<=mid,mid<j<=high]

知道这些就可以来分析分治策略了,先来看看是穿越中间的情况:

这种情况应该是从中间分别向左,向右进行求和,计算两个方向的最大求和与下标,保存后返回,如下图所示:

   通过上边的分析,我们不难发现,其实求最大子数组问题的子问题就是:求通过中间的最大数组问题。

然后咱们来验证一下如图所示:

 

 通过图片可以看出,从上到下分解问题到子问题,复杂度有所降低。

接下来通过伪代码实现一下:

FIND-MAX-CROSSING-SUBARRAY(A,low,mid,high)
left-sum = -∞
sum = 0
for i = mid downto low
{
    sum = sum+A[i]
    if (sum>left-sum){
        left-sum = sum
        max-left = i
       }
}
       
right-sum = -∞
sum = 0
for j=mid+1 to high
{
   sum = sum+A[j]
  if(sum>right-sum){
  right-sum = sum
  max-right = j
  }
}
return (max-left,max-right,left-sum + right-sum)

上边的伪代码实现的功能是,有中间开始分别向两边加和,求出最大的和,并且记录两边的索引,将信息返回 

FIND-MAXIMUM-SUBARRAY(A,low,high)
if(high == low){
     return (low,high,A[low])
}
else mid=[(low+high)/2]
    (left-low,left-high,left-sum) = FIND-MAXIMUM-SUBARRAY(A,low,mid)
    (right-low,right-high,right-sum) = FIND-MAXIMUM-SUBARRAY(A,mid+1,high)
    (cross-low,cross-high,cross-sum) = FIND-MAX-CROSSING-SUBARRAY(A,low,mid,high)
   if (left-sum>=right and left-sum>=cross-sum)
      return (left-low,left-high,left-sum)
   elseif(right-sum>=left-sum and right-sum>=cross-sum)
    return (right-low,right-high,right-sum)
   else return (cross-low,cross-high,cross-sum)

这一部分实现的是递归调用 FIND-MAX-CROSSING-SUBARRAY(A,low,mid,high)分解问题为子问题,并且判断哪个为最大子数组.

二.Java实现

package Main;

public class Test {

    public static void main(String[] args) {
         int[] nums = {13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7};
         anser an = new anser();
         an = FineMaxSubarray(nums,0,15); //以下标为基准
         System.out.print(an.getIndex1()+" "+an.getIndex2()+" "+ an.getMax());
    }

    public static anser FineMaxCorssingSubarray(int[] nums,int low,int mid,int high){
        int leftMax = -100;    //数组中间向左方向最大值初始化
        int sum = 0;            //用来统计数值
        int leftMaxindex = 0;  //记录左最大索引
        int rightMaxindex = 0;   //记录右边最大索引
        for(int i=mid;i>low;i--){   //进行左统计
            sum=sum+nums[i];
            if(sum>leftMax){
                leftMax = sum;
                leftMaxindex = i;
            }
        }
        int rightMax = -100;
        sum = 0;
        for(int i=mid+1;i<high;i++){  //进行右统计
            sum=sum+nums[i];
            if(sum>rightMax){
                rightMax = sum;
                rightMaxindex = i;
            }
        }
        anser an = new anser(leftMaxindex,rightMaxindex,leftMax+rightMax);  //建立类保存值
        return an; //返回得到的类
    }

    public static anser FineMaxSubarray(int[] nums,int low,int high){
        if(high == low){
            return new anser(low,high,nums[low]);
        }
        else{
            int mid = (low+high)/2;
            anser anleft = new anser();
            anleft = FineMaxSubarray(nums,low,mid);   //递归向左
            anser anright = new anser();
            anleft = FineMaxSubarray(nums,mid+1,high);  //递归向右
            anser ancross = new anser();
            ancross = FineMaxCorssingSubarray(nums,low,mid,high);   //获取中间
            if(anleft.getMax()> ancross.getMax()&&anleft.getMax()> anright.getMax()){  //进行sum比较
                return anleft;
            }else if(anright.getMax()> ancross.getMax() && anright.getMax()> anleft.getMax()){
                return anright;
            }else{
                return ancross;
            }
        }
    }

}

运行结果:

三.C++实现

#include<iostream>
using namespace std;

typedef struct {
	int index1;
	int index2;
	int max;
}anser;


anser FineMaxCorssingSubarray(int* nums, int low, int mid, int high) {
	int leftmax = -1000;
	int leftmaxindex = 0;
	int sum = 0;
	for (int i = mid; i > low; i--) {
		sum += nums[i];
		if (sum > leftmax) {
			leftmax = sum;
			leftmaxindex = i;
		}
	}
	int rightmax = -1000;
	int rightmaxindex = 0;
	sum = 0;
	for (int i = mid + 1; i < high; i++) {
		sum += nums[i];
		if (sum > rightmax) {
			rightmax = sum;
			rightmaxindex = i;
		}
	}
	anser an;
	an.index1 = leftmaxindex;
	an.index2 = rightmaxindex;
	an.max = leftmax + rightmax;
	return an;
}

anser FineMaxSubarray(int* nums, int low, int high) {
	if (low == high) {
		anser an;
		an.index1 = low;
		an.index2 = high;
		an.max = nums[low];
		return an;
	}
	int mid = (low + high) / 2;
	anser left = FineMaxSubarray(nums, low, mid);
	anser right = FineMaxSubarray(nums, mid + 1, high);
	anser cross = FineMaxCorssingSubarray(nums, low, mid, high);
	if (left.max > right.max && left.max > cross.max) {
		return left;
	}
	else if (right.max > left.max && right.max > cross.max) {
		return right;
	}
	else {
		return cross;
	}
}

int main() {
	int nums[] = {13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7};
	anser an =FineMaxSubarray(nums, 0, size(nums) - 1);
	cout << an.index1 << " " << an.index2 << " " << an.max;
	return 0;
}

运行结果:

 ------------------------------------------------------------------------------------------------------------

最后希望大家可以参与一下投票,来决定下一篇文章用什么语言实现

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

RIDDLE!

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

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

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

打赏作者

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

抵扣说明:

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

余额充值