LeetCode刷题之旅(简单-13):最大子序和
原创
©著作权归作者所有:来自51CTO博客作者后台技术汇的原创作品,请联系作者获取转载授权,否则将追究法律责任
2019年6月8日
目录
题目:
解决方法1:暴力累计遍历法
思路:
性能结果:
解决方法2:动态规划
思路:
性能结果:
解决方法3:分治法(类似二分法)
思路:
性能结果:
解决方法4:其他网友的解法:利用中间数组过渡比较
思路:
性能结果:
小结:
题目:
解决方法1:暴力累计遍历法
public class MaximumSubordinateSum {
public static void main(String[] args){
List<Integer> list = new ArrayList<>();
list.add(-2);
list.add(1);
// list.add(-3);
// list.add(4);
// list.add(-1);
// list.add(2);
// list.add(1);
// list.add(-5);
// list.add(4);
int[] array = list.stream().mapToInt(Integer::intValue).toArray();
int sum = maxSubArray(array);
System.out.println("max = "+ sum);
}
public static int maxSubArray(int[] nums) {
if (nums.length <= 0){
return 0;
}
Integer max = nums[0];
for (int i = 1 ; i<nums.length; i++){
int temp = nums[i];
if (temp > max){
max = temp;
}
for (int j= i+1;j<nums.length;j++){
temp += nums[j];
if (temp > max){
max = temp;
}
}
}
return max;
}
}
思路:
- 基于JDK8的stream,用mapToInt对于Integer元素转int,创建数组入参;
- 时间复杂度:O(n) = n*n;肉眼可见,结果不尽人意。(O(n)姑且木有实现,分治呢?)
性能结果:
解决方法2:动态规划
/**
* 主要是利用逐步求解,以连续数组结束位置为每一步的解,
* sum其实就是记录了上一步骤的解,在这一步骤进行对比,如果上一步骤的解<0则舍弃。
* 最终得到这一步骤解,与之前步骤解的最大值res进行比较,保存当前的最优解。
* */
public static int maxSubArrayV2(int[] nums) {
int sum=0;
int res=nums[0];
// 1.一次遍历解决
for(int num:nums){
// 2.上次结果是否正数 ? 与新元素相加 : 取新元素
sum=sum>0?(sum+num):num;
// 3.该次结果与上次结果比较与赋值
if(res<sum){
res=sum;
}
}
return res;
}
思路:
- 主要是利用逐步求解,以连续数组结束位置为每一步的解,sum其实就是记录了上一步骤的解,在这一步骤进行对比,如果上一步骤的解<0则舍弃。最终得到这一步骤解,与之前步骤解的最大值res进行比较,保存当前的最优解。
- 这道题根据题目关键词,“最大”“连续”,可以判断是一道动态规划。
性能结果:
性能提高明显。
解决方法3:分治法(类似二分法)
public static void main(String[] args){
List<Integer> list = new ArrayList<>();
list.add(-2);
list.add(1);
// list.add(-3);
// list.add(4);
// list.add(-1);
// list.add(2);
// list.add(1);
// list.add(-5);
// list.add(4);
int[] array = list.stream().mapToInt(Integer::intValue).toArray();
int sum = maxSubArrayPart(array,0,array.length-1);
System.out.println("max = "+ sum);
}
//-------------------分治法----------------------
/**
* 通过递归分治不断的缩小规模,问题结果就有三种,左边的解,右边的解,以及中间的解(有位置要求,从中介mid向两边延伸寻求最优解),
* 得到三个解通过比较大小,等到最优解。
* */
private static int maxSubArrayPart(int[] nums,int left,int right){
if(left==right){
return nums[left];
}
int mid=(left+right)/2;
return Math.max(
maxSubArrayPart(nums,left,mid),
Math.max(
maxSubArrayPart(nums,mid+1,right),
maxSubArrayAll(nums,left,mid,right)
)
);
}
//左右两边合起来求解
private static int maxSubArrayAll(int[] nums,int left,int mid,int right){
int leftSum=Integer.MIN_VALUE;
int sum=0;
for(int i=mid;i>=left;i--){
sum+=nums[i];
if(sum>leftSum){
leftSum=sum;
}
}
sum=0;
int rightSum=Integer.MIN_VALUE;
for(int i=mid+1;i<=right;i++){
sum+=nums[i];
if(sum>rightSum){
rightSum=sum;
}
}
return leftSum+rightSum;
}
思路:
- 通过递归分治不断的缩小规模,问题结果就有三种,左边的解,右边的解,以及中间的解(有位置要求,从中介mid向两边延伸寻求最优解),得到三个解通过比较大小,等到最优解。
- 解法3的时间复杂度是nlog(n);
- 分治的计算工作主要在中间部分的计算中, 左右部分的分治事实上没什么贡献;
性能结果:
解决方法4:其他网友的优秀解法
class Solution {
public int maxSubArray(int[] nums) {
int[] dp = new int[nums.length+1];
for (int i = 0; i < nums.length; i++) {
dp[i] = nums[i];
}
for (int i = 0; i < nums.length-1; i++) {
dp[i+1] = Math.max(dp[i+1], dp[i]+nums[i+1]);
}
int maxx = dp[0];
for (int i = 1; i < nums.length; i++)
maxx = Math.max(maxx, dp[i]);
return maxx;
}
}
思路:
- 拷贝nums到dp数组;
- 从dp第二项开始,元素取(nums逐项)和(相邻两个元素之和)的最大值;其本质就是通过中间数组进行了一次相加(delta);(我感觉还是未能描述清楚作者的意图)
性能结果:
小结:
只能说,好的代码,性能优化的非常好,思路也是新奇的很;多练习,多思考,从现在出发吧。