贪心算法推到过程

七月林奔老师 算法优化步骤总结(听的最好的数据结构教学课,没有之一)
demo
以leetcode 53 maximum subarray为例
1 写出最完整的遍历过程
class Solution {
    public int maxSubArray(int[] nums) {
        
        int maxsum=0;
            for(int st=0;st<nums.length;++st){//st代表开始符
                for(int ed=st;ed<nums.length;++ed)  {ed代表结束符
                    int sum=0;
                     for(int i=st;i<=ed;++i){
                        sum += nums[i];
                        if(sum>maxsum)
                            maxsum=sum;
                    }
            }
        }
        return maxsum;
    }
     
}
2 第一重优化
计算思想
a代码demo1加粗地方是重复st与ed之间的和,并找出最大值
如图

st


ed




-7
16
-6
31
21
-9
-6
31
优化思想为求和转化为求差
即st 与 ed之间的最大和相当于ed之前的和减去st之前的和 如下图maxsub=sumst-sumed
sumst代表st之前的所有数字和
sumed同理

st


ed





-7
9
3
34
55
46
40
71

-7
16
-6
31
21
-9
-6
31



技巧
第一层for循环可以再循环外设置变量记录下sumst,并保存在第一层循环外。
第二层for循环可以再记录下sumst与ed位置的数字和,并保存在第二层循环外。
相当于数组中第2个到第4个的和等于sum4-sum1
但是这种减法有两种方式
分别是
固定开始标签st往后查,查到最大的

class Solution {
    public int maxSubArray(int[] nums) {
        int maxsum = 0;
        int sumst = 0;
        for (int st = 0; st < nums.length; ++st) {
            sumst += nums[st];
            int temsum = 0;
            for (int ed = st+1; ed < nums.length; ++ed) {//此处不论ed=st+1或者st都能得到正解,但是等于st会多计算,原因是,求得是最大的是求边界之间差的最大值,都要去计算区间内的最大值,但总区间不变。
                 temsum += nums[ed];
                if ((temsum - sumst) > maxsum) {
                    maxsum = temsum - sumst;
                }
            }
        }
        return maxsum;
    }
}  

 固定结束标签,往内查最大的值

class Solution {
    public int maxSubArray(int[] nums) {
        int maxsum = 0;
        int sumed = 0;

        for (int ed = 0; ed < nums.length; ++ed) {
            sumed += nums[ed];

            int sumst = sumst ;

            for (int st = 0; st < ed; ++st) {
                sumst += nums[st];
                if ((sumed - sumst) > maxsum) {
                    maxsum = sumed - sumst;
                }
            }
        }
        return maxsum;
    }
}

最后一种优化是
由于对于区间内任意数列之和sum等于sumed-sumst。这个很容易得出来,利用数学证明
sumst=a1+a2+a3+...at
sumed=a1+a2+a3+...aed
sum=sumed-sumst
由这种思想可以把求最大数列和转化为两个数列和的最大差
代码如下

class Solution {
    public int maxSubArray(int[] nums) {
        int maxsum = -214783647;
        int sumed = 0;
        int [] sumtotal = new int[nums.length+1];
        sumtotal[0]=nums[0];
        
        for (int i=1;i<nums.length;++i){
            sumtotal[i+1]=nums[i]+sumtotal[i];
        }

        for (int st = 0; st < nums.length; ++st) {
            int stsum=sumtotal[st];
            for (int ed = st; ed < nums.length; ++ed) {
                int edsum=sumtotal[ed];
                
                if ((edsum - stsum) > maxsum) {
                    maxsum = edsum - stsum;
                }
            }
        }
        return maxsum;
    }     
}

由以上思想再次转换,求sumst与sumed最大差等于sumed减去最小的sumst

class Solution {
    public int maxSubArray(int[] nums) {
        int maxsum = -214783647;
        int sumed = 0;
        int [] sumtotal = new int[nums.length+1];
        sumtotal[0]=nums[0];
        
        for (int i=1;i<nums.length;++i){
            sumtotal[i+1]=nums[i]+sumtotal[i];
        }
        for (int ed = 0; ed < nums.length; ++ed) {
            int min=0;
            for (int st = 0; st < ed; ++st) {
                if (sumtotal[st] <min) {
                    min=sumtotal[st];
                }
                if(sumtotal[ed]-min>maxsum){
                    maxsum=sumtotal[ed]-min;
                }
            }
        }
        return maxsum;
    }     
}
精彩时刻
以上的最内层循环反复叠加求其中的最小值,可以用增量方式来代替计算最大差
设置si是为了定义第一个初始变量,最开始第一个的时候si为0,如果不定义si,如果第一个为负,最小值为第一个本身,相当于是sj-minsi=sj-sj=0,是错误的。
demo
第一轮
minsi=0








si=0
 








sj=-7
 







-7
9
3
34
55
46
40
71

-7
16
-6
31
21
-9
-6
31



minsi=-7








si=-7









sj=9







-7
9
3
34
55
46
40
71

-7
16
-6
31
21
-9
-6
31


class Solution {
    public int maxSubArray(int[] nums) {
        int maxsum = -2147483647;
        int sj = 0;
        int si = 0;
        int minsi = 0;
        for (int ed = 0; ed < nums.length; ++ed) {
            sj += nums[ed];
            int min = 0;
            if (si < minsi) {
                minsi = si;
            }
            if ((sj - minsi) > maxsum) {
                maxsum = (sj - minsi);
            }
            si += nums[ed];
        }
        return maxsum;
    }
}
最精彩的时刻
有常规的去冗余算法变成贪心算法
利用替换变量
sum=si-minsi转换可以得到
class Solution {
    public int maxSubArray(int[] nums) {
        int maxsum = -2147483647;
         int sj = 0;
        int si = 0;
         int minsi = 0;
        int sum= si-minsi=0=si
        for (int ed = 0; ed < nums.length; ++ed) {
             sj += nums[ed];
             int min = 0;
             if (si < minsi) {    if(si-minsi<0)  if(sum<0)
                 minsi = si;        minsi-si=0;  sum=0;
            }
            if ( (sj - minsi) > maxsum) { (si+nums[ed]-minsi>maxsum)  (sum + nums[ed]>maxsum);
                maxsum = (sj - minsi);     (si+nums[ed]-minsi)  sum+nums[ed];
            }
            si += nums[ed]; si-nums[ed]=si
        }
        return maxsum;
    }
}
及代码
class Solution {
    public int maxSubArray(int[] nums) {
        int maxsum = -2147483647;
        int si = 0;
        int sum= 0;
        for (int ed = 0; ed < nums.length; ++ed) {
                  if(sum<0)
                  sum=0;
            }
            if ( (sum + nums[ed]>maxsum ) { 
                maxsum = sum+nums[ed];     
            }
            sum=0; 
        }
        return maxsum;
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值