分治策略(算法)
不积跬步无以至千里,不积小流无以成江海,程序人生的精彩需要坚持不懈地积累!
分治策略
在分治策略中我们递归的求解一个问题,在每层递归中应用如下三个步骤:
分解:将问题划分成为一些子问题的形式,子问题形式与原问题一样,只是规模更小。
解决:递归的求解子问题。如果问题的规模足够小,则停止递归,直接求解。
合并:将子问题的解组合形成原问题的解。
求解递归的O渐近界方法:
【1】猜测代入法
【2】递归树法
【3】主方法 使用公式
最大字数组和的求解
package algorithm.java.ch04;
/**
* @author LbZhang
* @version 创建时间:2015年12月21日 下午7:56:23
* @description 最大子数组的和 问题是这样的:一个整数数组中的元素有正有负,在该数组中找出一个连续子数组,要求该子数组中各元素的和最大,
* 这个子数组便被称作最大子数组。比如数组{2,4,-7,5,2,-1,2,-4,3}的最大子数组为{5,2,-1,2},
* 最大子数组的和为5+2-1+2=8。
*/
public class MaxSubArray {
public static void main(String[] args) {
int[] numArr = { 2, 4, -7, 5, 2, -1, 2, -4, 3 };
System.out.println("暴力求解:");
baoliHandle(numArr);
System.out.println("线性求解:'");
linarHandle(numArr);
System.out.println("分治求解:");
divHandle(numArr);
}
/*
* 暴力求解办法
*/
public static int baoliHandle(int[] arr) {
int i, j;
int len = arr.length;
int maxSum = 0;
int begin = 0, end = 0;
for (i = 0; i < len; i++) {
int Cursum = 0;// 每一个作为起点的累加的数据和的临时保存
for (j = i; j < len; j++) {
Cursum += arr[j];
if (Cursum > maxSum) {
maxSum = Cursum;
begin = i;
end = j;
}
}
}
System.out.println("最大字数组:");
for (i = begin; i <= end; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
System.out.println("MaxSum: " + maxSum);
return maxSum;
}
public static int divHandle(int[] arr) {
SubArray sa = new SubArray();
sa = findMaxSubArray(sa, arr, 0, arr.length - 1);
System.out.println("最大字数组:");
for (int i = sa.getBegin(); i <= sa.getEnd(); i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
System.out.println("MaxSum: " + sa.getSum());
return sa.getSum();
}
/**
* 递归调用函数
*
* @param sa
* @param arr
* @param i
* @param j
*/
private static SubArray findMaxSubArray(SubArray sa, int[] arr, int left,
int right) {
if (left == right) {
sa.setBegin(left);
sa.setEnd(right);
sa.setSum(arr[left]);
return sa;
} else {
int mid = (left + right) / 2;
SubArray saleft = findMaxSubArray(sa, arr, left, mid);
SubArray saright = findMaxSubArray(sa, arr, left, mid);
SubArray sacross = findCrossingMaxSubArray(sa, arr, left, mid,
right);
if (saleft.getSum() > saright.getSum()
&& saleft.getSum() > sacross.getSum()) {
return saleft;
} else if (saright.getSum() > saleft.getSum()
&& saright.getSum() > sacross.getSum()) {
return saright;
} else {
return sacross;
}
}
}
private static SubArray findCrossingMaxSubArray(SubArray sa, int[] arr,
int left, int mid, int right) {
int sum=0;
int begin=0;
int end=0;
int leftsum = 0;
for(int i=mid;i>=left;i--){
sum=sum+arr[i];
if(sum>leftsum){
leftsum=sum;
begin=i;
}
}
int rightsum = 0;
sum=0;
for(int i=mid+1;i<=right;i++){
sum=sum+arr[i];
if(sum>rightsum){
rightsum=sum;
end=i;
}
}
sa.setBegin(begin);
sa.setEnd(end);
sa.setSum(leftsum+rightsum);
return sa;
}
/**
* 最优方法,时间复杂度O(n)和最大的子序列的第一个元素肯定是正数因为元素有正有负,因此子序列的最大和一定大于0
*
* @param arr
* @return
*/
public static int linarHandle(int[] arr) {
int i;
int len = arr.length;
int maxSum = 0;
int curSum = 0;
int begin = 0, end = 0;
for (i = 0; i < len; i++) {
curSum += arr[i];
if (curSum > maxSum) {
maxSum = curSum;
end = i;
}
// 如果累加和出现小于0的情况,
// 则和最大的子序列肯定不可能包含前面的元素
// 这时将累加和置0,从下个元素重新开始累加
if (curSum < 0) {
curSum = 0;
begin = i + 1;// 最小从下一个开始
}
}
System.out.println("最大字数组:");
for (i = begin; i <= end; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
System.out.println("MaxSum: " + maxSum);
return maxSum;
}
}
运行结果:
踏实 踏踏实实~