分治思想
求最大连续子数组和:
方法一:暴力法:
暴力法求最大和,就是利用循环将一个数组的无数组合可能性的和都求出来,进行比较,求出当下最大和的最大值
如代码中的第一个大循环,就是以i为起始,以j为结尾的元素下标,然后将所有以i为开头,以j为结尾的的数组的组合都求出来,算出来一个最大值,然后,以i+1,为开头,继续进行,直到所有的都算出来为止
比如说一个数组arr[1,2,-3],如图:
import java.util.Scanner;
public class baoli {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int k=sc.nextInt();
//第一行输入原始数组的长度
int[] arr=new int[k];
//确定数组的长度
for(int i=0;i<k;i++) {
arr[i]=sc.nextInt();
}
//输入
System.out.println(maxsum(arr));
}
public static int maxsum(int[] arr) {
int max=0;
int cur=0;
int left = 0;
int right = 0;
//以每一个元素为开头,进行大量的重复
//以i为循环累加的元素下标的开头,以j为结尾
for(int i=0;i<arr.length;i++) {
for(int j=i;j<arr.length;j++) {
int arrsum=0;
//arrsum即当前的最大和
for(int k=i;k<=j;k++) {
arrsum+=arr[k];
//srrsum不断累加
}
if(arrsum>cur) {
//arrsum达到最大值以后,将这个数值赋给cur
cur=arrsum;
//在当前停下的,就是起始值
left=i;
right=j;
}
}
}
for(int l=left;l<=right;l++) {
max+=arr[l];
}
return max;
}
}
方法二:神奇的方法
这个是从B站上学到的,附上链接,这个方法是设定两个值,一个cur,一个max,cur用来不断移动累加取得当前的最大值,并且当cur为负数的时候就重置为0,而max则是初始为最小值,也就是MIN_VALUE,之后与cur不断比较,存储cur的最大值,而最后,max的值就是最大和。
比如说一个数组arr[1,2,3,-4,-5,6,7],如图:
import java.util.Scanner;
public class zishuzu {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int k=sc.nextInt();
//第一行输入原始数组的长度
int[] arr=new int[k];
//确定数组的长度
for(int i=0;i<k;i++) {
arr[i]=sc.nextInt();
}
//输入
System.out.println(maxsum(arr));
}
public static int maxsum(int[] arr) {
if(arr==null||arr.length==0) {
return 0;
}
int max=Integer.MIN_VALUE;
//max是最小值
int cur=0;
//cur的起始值为0
for(int j=0;j!=arr.length;j++) {
cur+=arr[j];
//cur不断累积
max=Math.max(max, cur);
//没累加一次就与max进行一次比较,max为二者中最大的数
cur=cur<0?0:cur;
//cur如果一旦小于0.就重置归零
}
return max;
//最后返回的max就是最大子数组之和
}
}
方法三:分治法:
分治法是《算法导论》本章很重要的一个知识点,但是。。。嗯。。。菜鸡发现道理我都懂,代码不会写,后参考大神代码理解。
分治法求最大和就是寻找到最大连续子数组,只要找到这个最大连续子数组,就可以求和了,而最大连续子数组的位置,按照分治思想有三种可能性:1.在原始数组的左端部分,2.在原始数组的中间位置,3.在原始位置的右部分
所以从中间位置划分,开始游览,左边部分与右边部分的最大和的,而中间位置其实就是左右部分的最大值的相加, 递归相加即可。
比如说一个数组:arr[]={1,2,-3,-4,-5,6,7,-8,-9,-10},如图:
import java.util.Scanner;
public class fenzhi {
//三个部分进行比较
public static int max3(int a, int b ,int c) {
int max=a;
if(b>max) {
max=b;
}
if(c>max) {
max=c;
}
return max;
}
public static int findmaxSubSum(int[] arr,int left, int right) {
int maxLeftSum,maxRightSum; //左边和右边最大和
int maxLeftBorderSum, maxRightBorderSum; //含中间边界的左右部分最大和
int leftBorderSum, rightBorderSum; //含中间边界的左右部分当前和的值
int i,center;
//如果左右下标相等,左右相加的时候,返回一个左边的
if(left == right) {
return arr[left];
}
center = (left + right)/2;
//中间值,从这里开始
//找出左部最大值
maxLeftBorderSum =0;
leftBorderSum =0;
for(i=center;i>=left;i--) {
leftBorderSum += arr[i];
//让左边部分定义的这个和不断进行累加
if(leftBorderSum > maxLeftBorderSum) {
//并将最大值赋给
maxLeftBorderSum = leftBorderSum;
}
}
//找出右部最大值
maxRightBorderSum =0;
rightBorderSum =0;
for(i=center+1;i<=right;i++) {
rightBorderSum +=arr[i];
if(rightBorderSum>maxRightBorderSum) {
maxRightBorderSum = rightBorderSum;
}
}
//递归求左右部分最大值
maxLeftSum =findmaxSubSum(arr, left, center);
maxRightSum = findmaxSubSum(arr, center+1, right);
//左部分,右部分,中间值跨界部分
return max3(maxLeftSum, maxRightSum, maxLeftBorderSum+maxRightBorderSum);
}
public static int maxSubSum(int arr[],int len) {
return findmaxSubSum(arr, 0, len-1);
//数组的长度、数组左部分,即left,数组右部分,即right
}
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int k=sc.nextInt();
//第一行输入原始数组的长度
int[] arr=new int[k];
//确定数组的长度
for(int i=0;i<k;i++) {
arr[i]=sc.nextInt();
}
System.out.println(maxSubSum(arr, k));
}
}