LeetCode 最大子序和

https://leetcode-cn.com/problems/maximum-subarray/description/

我的解决方案:这就道破题,我做了一个通宵。。。如果看不懂代码,可以看一下我下面的思路变迁,都是一点一点想出来的

class Solution { 
    public static int getRes3(int[] nums, int maxRes) {
        maxRes=Integer.max(nums[0], maxRes);
        maxRes=Integer.max(nums[2], maxRes);
        maxRes=Integer.max(nums[0]+nums[1]+nums[2], maxRes);
        return maxRes;
    }
    public static int getRes5(int[] nums, int maxRes) {
        maxRes = Integer.max(nums[0]+nums[1]
                +nums[2]+nums[3]+nums[4], maxRes);
        maxRes = Integer.max(nums[0]+nums[1]+nums[2], maxRes);
        maxRes = Integer.max(nums[2]+nums[3]+nums[4], maxRes);
        maxRes = Integer.max(nums[0], maxRes);
        maxRes = Integer.max(nums[2], maxRes);
        return Integer.max(nums[4], maxRes);
    }
    public static int recursion(int[] nums, int len, int maxRes) {
        if(len==3)
            return getRes3(nums, maxRes);
        if(len==5)
            return getRes5(nums, maxRes);
        if(nums[0]+nums[1]<=0)
            maxRes = Integer.max(nums[0], maxRes);
        else {
            nums[2]+=nums[0]+nums[1];
            maxRes = Integer.max(nums[0], maxRes);
        }
        if(nums[len-2]+nums[len-1]<=0)
            maxRes = Integer.max(nums[len-1], maxRes);
        else {
            nums[len-3]+=nums[len-1]+nums[len-2];
            maxRes = Integer.max(nums[len-1], maxRes);
        }
        int cnt=0;
        for(int i=2;i<len-2;i++) {
            nums[cnt++]=nums[i];
        }
        return recursion(nums, cnt, maxRes);
    }
    public static int maxSubArray(int[] nums) { 
        boolean flag = false;
        for(int i=0;i<nums.length;i++) {
            if(nums[i]>0) {
                flag = true;
                break;
            }
        }   
        if(flag==false) {
            int max = Integer.MIN_VALUE;
            for(int i=0;i<nums.length;i++)
                if(nums[i]>max) max=nums[i];
            return max;
        }
        int head=0;
        for(int i=0;i<nums.length;i++) {
            if(nums[i]>0) {
                head = i;
                break;
            }
        }
        int lenDec=0;
        for(int i=nums.length-1;i>=0;i--) {
            if(nums[i]>0) break;
            lenDec++;
        }
        int numLen = nums.length-lenDec;
        int cnt=0;
        int sum=nums[head];
        for(int i=head+1;i<numLen;i++) {
            //此处不能使用nums[i]来判断符号,因为0会把负数也弄到sum中
            while(sum*nums[i]>=0) {
                sum+=nums[i];
                i++;
                if(i==numLen) break;
            }
            if(i==numLen) break;
            nums[cnt++]=sum;
            //跳出了while循环,说明符号发生了改变
            sum=nums[i];
        } 
        nums[cnt++] = sum;  
        int maxRes = Integer.MIN_VALUE;
        if(cnt==1)
            return nums[0]; 
        return recursion(nums, cnt, maxRes);
    }
    public static void main(String[] args) {
        int[] test = new int[] {-9,9,7,-8,-3,2,-9,1,7,-2,-9,7,-9,5,5,-3,9,0};
        System.out.println(maxSubArray(test));
    }
}

其实他就是一道水得不能再水的题:

class Solution {
    public int maxSubArray(int[] nums) {
        int max = Integer.MIN_VALUE;
        int tempSum = 0;
        for (int i = 0; i < nums.length; i++) {
            if (tempSum < 0) {
                tempSum = nums[i];
            } else {
                tempSum += nums[i];
            }
            max = Math.max(tempSum, max);
        }
        return max;
    }
}

我的内心是崩溃的。。。。。

我的思路:

package ads;

import java.text.Normalizer.Form;

class AlarmClock {
    public static void main(String args[]) {
        最长连续字串的难点在于,如何确定前后指针的移动
        为了消除干扰,我们会预处理数组,先处理掉不含正数的数组
        设置两个指针,front和rear,分别指向连续子串的首位
            向后拓展,当后和lSum值为正时,或者nums[index]>sum;
            将rear更新为当前index
            再来更新front值:
            Form head to rear : index
                rSum+=nums[head]+nums[index];
                当前和非正时,就需要更新head值了
                if(!(rSum>0))
                    head=index;
    }
}



产生了一个Idea,把数组分成一块一块的,
        正负交织
        然后再进行组合
        如果开头是负数,那就全部扔掉

        这样一来,数组就会越变越小
    eg:
        -2,1,-3,4,-1,2,1,-5,4
    ==> 1 -3 4 -1 3 -5 4
如果两者相加相较于单独比较大,那么就有必要相加   我们处理完的数组的长度总是奇数
            1 2 3 4 5 6 7 8 9 
            正负正负正负正负正
        我们的数组都应该是上面的这种形式,首位都不会存在负数
        if(nums[1]+nums[2]+nums[3]>nums[1]&&nums[1]+nums[2]+nums[3]>nums[3])
            nums[3]=nums[1]+nums[2]+nums[3];
            head = 3;
        else
            如果出现这种情况,就说明中间的那个负数的绝对值比较大,比左侧或右侧大
            if(左侧小于右侧)
                则12被抛弃
            否则  
                nums[1]称为一个候选结果res
                 head=3;
    789还是同样的道理,更新新的rear
    如果else 
        nums[9]比nums[7]大且大于nums[1](res),则nums[9]称为候选结果


        我们有一个任务就是确定123什么时候该合并,什么时候不该合并
            很明显if(1+2<=0)  不该合并 1作为单独候选结果存在
            else   合并  1同样还是作为候选结果单独存在
            head = 3;
    之所以要把1作为候选结果,是因为我们要考虑极端情况,2是一个很大的负数,这时候1可能就是一个很大的正数 而345678之和也不及1自己的值
    因此我们不能轻易就把1抛弃
    这是一个从两边向中间合并的过程
    我们可以使用递归,每次处理1237893和尾3  并比较出较大的后选结果
            候选结果挨个比较,筛选出最大的候选结果作为返回值
        int recursion(int[] nums, int len, int maxRes);
            当数组长度为5的时候,即可比较出最大值
            1 2 3 4 51+2+3+4+5 
                    1+2+3
                        3+4+5
                        1
                        3
                        5进行比较

public class Solution {
    public static int maxSubArray(int[] nums) { 
        boolean flag = false;
        for(int i=0;i<nums.length;i++) {
          if(nums[i]>0) flag = true;
        }
        if(flag==false) {
            int min = Integer.MIN_VALUE;
            for(int i=0;i<nums.length;i++)
                if(nums[i]>min) min=nums[i];
            return min;
        }   
        int front = 0;
        int rear = 0;
        int sum=0,rSum=0,lSum=0;
        for(int i=0;i<nums.length;i++) {
            if(nums[i]>0) {front=i;rear=i;rSum=nums[i];break;}
        }
        sum=rSum;
        if(rear==nums.length-1) return nums[front];
        //我当前的处理办法,只能是走一步停一步,一气呵成会产生问题
        //front的处理会出现错误,rear倒是没什么问题
        while(flag) {
            for(int i=rear+1;i<nums.length;i++) {
                if(i==nums.length-1) flag=false;
                lSum+=nums[i];
                //有一种致命的情况,我没有考虑到,就是后和为0,但是在后和中存在一个比sum更大的值
                //因此,我要在每次while循环的末尾求出sum值,然后在本for循环中比较nums[index]和sum的大小
                //如果nums[index]更大,直接更新rear值
                if(lSum>0) {rear = i;   lSum=0;break;}
                if(nums[i]>sum) {rear=i;}
                //应该清空lSum值,把负值扔掉
                lSum=0;
            }
            for(int i=front+1;i<rear;i++) {
                rSum+=nums[i];
                if(!(rSum>0)) {
                    front=i ;
                    rSum=nums[front]; 
                }
            }
            rSum=nums[front];
            if(!(rSum>0)) { front+=1;rSum=nums[front];}
            sum=0;
            for(int i=front;i<=rear;i++) {
                sum+=nums[i]; 
            }   
        }
        sum=0;
        for(int i=front;i<=rear;i++) {
            sum+=nums[i]; 
        }   
        return sum;
    }
    public static void main(String[] args) {
        int[] test = new int[] {2,-1,3,-1};

        System.out.println(maxSubArray(test));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值