七月林奔老师 算法优化步骤总结(听的最好的数据结构教学课,没有之一)
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 si = 0;
int sum=
si-minsi=0=si;
for (int ed = 0; ed < nums.length; ++ed) {
}
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;
}
}