任意重循环(循环阶数不定、循环层数不定)
----哆啦刘小洋 原创,转载需说明出处
文章目录
- 1 简介
- 2 普通循环
- 3 任意重循环算法
- 4 任意重循环代码
- 4.1 准备
- 4.2 第2节代码重写
- 5 模块化
- 5.1 AnyLoop1
- 5.2 AnyLoop2
- 5.3 更加通用的模块化
- 5.3.1 回调函数准备
- 5.3.2 回调函数版
1 简介
循环是程序中最重要的结构之一。我曾经碰到过一次特殊需求,在解决问题时,根据问题中参数不同,需要用到不同层数的循环,也就是说循环层数不定。
因此本人实现了一个任意重循环的代码,以方便解决问题时可以直接调用。
接下来,我一步一步的说明实现的过程和几个不同的版本。
(不想理解的话,可以直接跳过2-3节。我理解你的不想理解。)
2 普通循环
先看一下普通循环,如下:
for(i=0; i<2; ++i)for(j=0; j<3; ++j)for(k=0; k<4; ++k)printf("%d %d %d \n", i, j, k);分析一下该代码:
1)循环时,总是增加最内层的循环量k。
2)k增加后,判断k是否超范围,如果超范围,则上一层循环量j加1,判断j是否超范围,如果超范围,则上一层循环量加1…。
3)直到某层循环加1后不超范围(比如i加1后为1),这时重新执行这一层里面的所有循环(j、k重新从0开始)。
4)某一次循环后,按前3步,所有层的循环量都超范围了(这里是i等于2了),则循环退出。
3 任意重循环算法
我们按照普通循环的思路,可以得到任意重循环算法:
while(1) {if(最外层的循环量超范围) break//(循环结束)循环体内的执行代码… 最内层循环量++ //k++ if(最内层循环量超范围) { for(上一层循环) {//遍历上层循环 j, i 该层循环量++ if(该层循环量没有超范围) { 重置该层里面层的循环量 break//(遍历结束) } } } }4 任意重循环代码
4.1 准备
还需要做点准备工作。我们用动态数组存储每一层的循环量。也用动态数组存储每一层循环的起始、结束值。
还是以第2节的代码为例,该代码中i,j,k我们用动态数组:
循环起始、结束值也用动态数组,我们设置起始值可以不从0开始,可提高实用性。
Int* pStartIndex = new int[3]; Int* pEndIndex = new int[3]; pStartIndex[0] = pStartIndex[1] = pStartIndex[2] = 0; pEndIndex[0] = 1; pEndIndex[1] = 2; pEndIndex[2] = 3;注意,我们约定:
1)循环包括结束值,相当于(for i=0; i<=…; ++i)。
2)pStartIndex[0]是最外层循环量。
4.2 第2节代码重写
int i, j, n = 3; int* pStartIndex = new int[n]; int* pEndIndex = new int[n]; pStartIndex[0] = pStartIndex[1] = pStartIndex[2] = 0; pEndIndex[0] = 1; pEndIndex[1] = 2; pEndIndex[2] = 3; int* pCurIndex = new int[n]; //初始化循环变量 for(i=0; i<n; ++i)pCurIndex[i] = pStartIndex[i]; //循环 while(1) {//如果最外层循环超范围,循环也就结束if (pCurIndex[0] > pEndIndex[0])break;for(i=0; i<n; ++i)printf(" %d", pCurIndex[i]);printf("\n");//最里层循环量加1pCurIndex[n-1]++;if (pCurIndex[n-1] > pEndIndex[n-1]) {for (i = n-2; i >= 0; --i) {pCurIndex[i]++;if (pCurIndex[i] <= pEndIndex[i]) {//重置比该层更里面的每层循环量for (j = i + 1; j < n; ++j)pCurIndex[j] = pStartIndex[j];break;}}} }5 模块化
用一个子函数来实现。函数传递每层循环的起始、结束值以及循环层数。与第4节相比,略微优化了一下跳出循环的判断代码。
5.1 AnyLoop1
该函数要求每层循环的循环变量必须从小到大:
/* \* 任意重循环函数1, 每层循环必须是由小到大 */ void AnyLoop1(int* pStartIndex, int* pEndIndex, int n) { /* \* pStartIndex:每层循环的起始索引值,一般为0。相当于普通循环的 i = 0;pStartIndex[0]对应最外层循环 \* pEndIndex:每层循环的结束索引值,循环要用到该值后才结束这一层,相当于 i<= pEndIndex[...]。 \* n:循环层数。 */ int i, j;//循环控制变量,相当于一般循环中的i j k...int* pCurIndex = new int[n];//循环控制变量初始化for (i = 0; i < n; ++i)pCurIndex[i] = pStartIndex[i];//检查循环量,如果有一层根据循环量计算出来的循环次数小于或等于0,则判定整体循环次数为0for (i = 0; i < n; ++i){if (pStartIndex[i] > pEndIndex[i]){delete[] pCurIndex;return;}} //while (1){//!!! do it !!!printf("loop: ");for (i = 0; i < n; ++i)printf("%d ", pCurIndex[i]);printf("\n");//!!! do it !!! for (i = n-1; i >= 0; --i){if (pCurIndex[i] < pEndIndex[i]){//1000pCurIndex[i]++;//重置比该层更里面的每层循环量for (j = i + 1; j < n; ++j)pCurIndex[j] = pStartIndex[j];break;}} //如果i循环用完也未执行到1000处,循环也就结束了if (i < 0)break;}delete[] pCurIndex; }主函数:
int main() {int anStartIndex[] = { 0, 0, 0 };int anEndIndex[] = { 1, 2, 3 };AnyLoop1(anStartIndex, anEndIndex, 3); }输出结果:
loop: 0 0 0 loop: 0 0 1 loop: 0 0 2 loop: 0 0 3 loop: 0 1 0 loop: 0 1 1 loop: 0 1 2 loop: 0 1 3 loop: 0 2 0 loop: 0 2 1 loop: 0 2 2 loop: 0 2 3 loop: 1 0 0 loop: 1 0 1 loop: 1 0 2 loop: 1 0 3 loop: 1 1 0 loop: 1 1 1 loop: 1 1 2 loop: 1 1 3 loop: 1 2 0 loop: 1 2 1 loop: 1 2 2 loop: 1 2 35.2 AnyLoop2
如果需要支持每层循环可以是从小到大,也可以是从大到小,则需要对AnyLoop1做点小修改。我们用一个符号变量(取值为1和-1)来控制每一层循环变量增加或减小。
/* \* 任意重循环函数2, 每层循环可以是由小到大,也可以是由大到小 */ void AnyLoop2(int* pStartIndex, int* pEndIndex, int n) {/*\* pStartIndex:每层循环的起始索引值,一般为0。相当于普通循环的 i = 0;pStartIndex[0]对应最外层循环\* pEndIndex:每层循环的结束索引值,循环要用到该值后才结束这一层,相当于 i<= pEndIndex[...]。\* n:循环层数。*/int i, j;//循环控制变量,相当于一般循环中的i j k...int* pCurIndex = new int[n];//循环控制变量初始化for (i = 0; i < n; ++i)pCurIndex[i] = pStartIndex[i]; //用符号(+1 -1)来控制每层循环是增加还是减小循环量int* pSign = new int[n]; //确定每层是由小到大,还是由大到小for (i = 0; i < n; ++i)pSign[i] = (pStartIndex[i] <= pEndIndex[i] ? 1 : -1); //while (1){//!!! do it !!!printf("loop: ");for (i = 0; i < n; ++i)printf("%d ", pCurIndex[i]);printf("\n");//!!! do it !!! for (i = n-1; i >= 0; --i){if (pCurIndex[i] * pSign[i] < pEndIndex[i] * pSign[i]){//1000pCurIndex[i] += pSign[i];//重置比该层更里面的每层循环量for (j = i+1; j < n; ++j)pCurIndex[j] = pStartIndex[j];break;}} //如果i循环用完也未执行到1000处,循环也就结束了if (i < 0)break;}delete[] pCurIndex;delete[] pSign; } int main() {int anStartIndex[] = { 0, 0, 0 };int anEndIndex[] = { -1, 2, -3 };AnyLoop2(anStartIndex, anEndIndex, 3); }5.3 更加通用的模块化
(如果不愿把事情搞的太复杂,可以不用再伤脑经继续看了,到此结束! )
执行代码目前还在循环体里面,在实际应用时,需要拷贝上述代码,并重写循环体内的执行代码,这当然可以,当需要更好的性能时,这也是我推荐的方法。
但还可以用回调函数在进一步模块化,如果你刚好又学习到回调函数,值得一看。
5.3.1 回调函数准备
我们循环体内执行时,只牵涉到每层循环量值,因此回调函数只传回去每层循环量值和循环层数即可。
定义回调函数:
typedef bool (*anyloop_callback_ptr)(int* pCurIndex, int n);
回调函数之所有用bool类型,是可以在循环时可以根据返回值终止循环。我们约定返回值为false,则终止。
5.3.2 回调函数版
为减少文章篇幅,我们仅对AnyLoop2增加回调函数的版本。要注意,回调函数性能相对低下,适用于总体循环次数不大的情况:
//回调函数anyloop_callback_ptr为bool型, 当返回false时,告诉循环不用继续了。 typedef bool (*anyloop_callback_ptr)(int* pCurIndex, int n);/* \* 任意重循环函数2回调函数版, 用回调函数实现通用型任意重循环。 */ void AnyLoop2(int* pStartIndex, int* pEndIndex, int n, anyloop_callback_ptr acp) {/*\* pStartIndex:每层循环的起始索引值,一般为0。相当于普通循环的 i = 0;pStartIndex[0]对应最外层循环\* pEndIndex:每层循环的结束索引值,循环要用到该值后才结束这一层,相当于 i<= pEndIndex[...]。\* n:循环层数。*/int i, j;//循环控制变量,相当于一般循环中的i j k...int* pCurIndex = new int[n];//循环控制变量初始化for (i = 0; i < n; ++i)pCurIndex[i] = pStartIndex[i];//用符号(+1 -1)来控制每层循环是增加还是减小循环量int* pSign = new int[n]; //确定每层是由小到大,还是由大到小for (i = 0; i < n; ++i)pSign[i] = (pStartIndex[i] <= pEndIndex[i] ? 1 : -1);//while (1){//!!! do it !!!if (!acp(pCurIndex, n))//回调return;//!!! do it !!! for (i = n-1; i >= 0; --i){if (pCurIndex[i] * pSign[i] < pEndIndex[i] * pSign[i]){//1000pCurIndex[i] += pSign[i];//重置比该层更里面的每层循环量for (j = i+1; j < n; ++j)pCurIndex[j] = pStartIndex[j];break;}} //如果i循环用完也未执行到1000处,循环也就结束了if (i < 0)break;} delete[] pCurIndex;delete[] pSign; }/* \* 回调函数。 */ bool test_anyloop_func(int* pCurIndex, int n) {printf("loop: ");for (int i = 0; i < n; ++i)printf("%d ", pCurIndex[i]);printf("\n");return true; }int main() {int anStartIndex[] = { 0, 0, 0 };int anEndIndex[] = { -1, 2, -3 };AnyLoop2(anStartIndex, anEndIndex, 3 , test_anyloop_func); }这样,便可以将循环函数单独存储为一个文件,实现模块化。
下载完整的代码资源。
总结
以上是生活随笔为你收集整理的任意重循环(循环阶数不定、循环层数不定)的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: 遇见未来 | PostgreSQL:一匹
- 下一篇: JVM垃圾清除算法