为了避免出现数组越界,建议使用i,i-1,i-2这样的变量,而非i,i+1,i+2
例如f(n)=f(n-1)+f(n-2)。
正循环都是如此。
循环的灵活使用
对于
For(变量初始化;循环继续的条件;迭代变量的移动){
}
这三个部分并不一定都要写上。你可以把它们空着,作为一个提醒。
For中i的次数可以表示轮数,但完全也可以灵活使用。
例如for(i=0;i<10;i++){
i++;
}
I会被加快。
尤其是在双指针这类问题中,更是如此。
来看下面这样一个for循环。并且是循环嵌套。
leetcode力扣第3297题。
for(i=0,j=0;word1[j];){
//每次专注分析指针j的一个元素
numHelpArr[cti(word1[j])]++;
flag=isEnough(chArr,numArr,chHelpArr,numHelpArr,newLen);
if(flag==0){
j++;
continue;
}
while(true){
ans+=len1-j;//达标就该结算一下
numHelpArr[cti(word1[i])]--;
i++;
flag=isEnough(chArr,numArr,chHelpArr,numHelpArr,newLen);
if(flag==0){
j++;
break;
}
//同指分析,提前结算
if(i==j){
ans+=len1-j;//提前结算
numHelpArr[cti(word1[i])]--;
i++;
j++;
break;
}
}
continue;
}
首先先来分析内层循环。直接一个死循环。当条件达到退出时再退出。
这样写有利于提高程序的灵活性。如果把flag放在while里面,很可能就无法实现功能了。
对于迭代变量I,j。通常应该先分析,再移动,以免出现越界。
例如探索指针j,就应该先分析j中的内容,再j++。
迭代变量i,i中的内容是在逐渐消退的,那么也是先对i进行消失,而后再移动i
while(true){
ans+=len1-j;//达标就该结算一下
numHelpArr[cti(word1[i])]--;
i++;
flag=isEnough(chArr,numArr,chHelpArr,numHelpArr,newLen);
if(flag==0){
j++;
break;
}
//同指分析,提前结算
if(i==j){
ans+=len1-j;//提前结算
numHelpArr[cti(word1[i])]--;
i++;
j++;
break;
}
}
对于上面这个循环,看到其中有很多部分时相同的,我们把相同的部分用中文字代替一下
while(true){
结算。
I清理叛徒。
I后查。
给flag判断状态。
if(flag==0){
j++;
break;
}
//同指分析,提前结算
if(i==j){
结算。
I清理叛徒。
I后查。
j++;
break;
}
}
将{
结算。
I清理叛徒。
I后查。
}视为一个整体,称为行动1.
那么就有,
while(true){
行动1
给flag判断状态。
if(flag==0){
j++;
break;
}
//同指分析,提前结算
if(i==j){
行动1
j++;
break;
}
}
现在我们考量,是否有可能把while中的true给换掉呢?
do{
行动1
给flag判断状态。
if(flag==0){
j++;
break;
}
} while(i!=j)
行动1
j++;
一定程度上可以,但是并不优雅。这是因为脱离循环有两种情况。一种是flag==0.也就是因为不满足条件而导致提前结束。另一种是i==j,也就是两者同指了,马上要分析完了。应该退出了。两者不同,退出所进行的行为也不同。因此不能将其合并。如果一定要去除内部的break,就必须在离开时指出其所属的退出标记,但是这样做,可读性并不高。
那么,行为与if判断的顺序是否可以调整呢?
while(true){
行动1
给flag判断状态。
if(flag==0){
j++;
break;
}
//同指分析,提前结算
if(i==j){
行动1
j++;
break;
}
}
事实上可以。也就是先判断再行动。但是,由于此种情况下,无论如何都会执行一次。必须在循环外先执行一次。然后再进入内部的循环。
行动1分为几种情况,
第一种,与迭代变量i无关的。
第二种,与某个迭代变量i有关的,且是读取关系的。
第三种,与某个迭代变量i有关的,且是读后修改关系的。
如果只想读取迭代变量i,那么移动这个语句,就不要越过修改这个迭代变量的语句。
更一般地,考量下面两个语句。
语句A(读变量1,读变量2,改变量3,改变量4)
语句B(读变量1,读变量2,改变量3,改变量4)
根据读改原则,可以判断其顺序是否是可变化的。也就是改变其顺序是否会影响其结果。
为何要做顺序的改变?如果顺序可以改变,则有些行为可以被整合。那就可以进行优化。
至于外层循环,则比较普通了。
不过说到一点想法,每个循环就是一个吞噬器,不断的往后吞噬一点。有点像迭代器。有点像数学归纳法的感觉。有点自动化的感觉。循环,才是自动化节约重复工作的核心。不过,计算机本身就是为了把工程自动化的。而循环,则是自动化中的自动化。
3297. 统计重新排列后包含另一个字符串的子字符串数目 I - 力扣(LeetCode)
欢迎读者留下自己对循环的理解!