如何改写多重循环

247 阅读1分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

原文链接:blog.csdn.net/roufoo/arti…

假设有类似下面的多重循环:

for(i=0; i<3; i++)
    for (j=2; j<6; j++)
       for (k=5; k<16; k++)
            for (l=3; l<5; l++)
              {
                 //do something with i,j,k,l
              }

像上面一共有4重循环,有时候可能更多,如何将任意多重循环用2重循环来改写呢?

方法1:

int main()
{
    vector<int> upper = {3,6,16,5};
    vector<int> lower = {0,2,5,3};
    vector<int> index = lower;
    int level;
 
    do{
        level =upper.size() - 1;
          
       //do something with index[]
       for (auto i : index) cout<<i<<" ";
        cout<<endl;
          
        while(level >= 0 && (++index[level] >= upper[level])) {
            index[level] = lower[level];
            level--;
        }
    } while (level >= 0);
 
    return 0;
}

输出结果为: 0 2 5 3 0 2 5 4 0 2 6 3 ...

2 5 15 3
2 5 15 4

上面的两层循环中,外层的do循环每次都从level=3开始,经过内层循环后检测level是不是还>=0。内层循环会做如下工作:

  1. ++index[level]
  2. 判断当前的level是不是已经到边界(upper[level]), 如果已到边界,将index[level]重新置为起始值,并将level减1,也就是到了上面一层; 否则跳过内层循环。
  3. 什么时候终止外层循环呢? 当level < 0时。而level<0只有可能内部的while循环总共执行了4次已经,即index[0], index[1], index[2], index[3]分别和lower[0], lower[1], lower[2], lower[3]相等后,level再--,此时level = -1。

如果还想不明白,在//do something的地方打印出index[0],index[1], index[2], index[3]即可,它们分别对应前面的i,j,k,l。也可以用下面的方法,原理差不多:

方法2:

注意do while循环只有当level = -1才终止。此时对应所以的组合都打印完毕,即从0,2, 6, 5打印到2,5,15,4,最后当index[]={2,5,15,4}时,对应for循环内部level--已经执行了4次,然后level再--,此时level = -1。注意没有打印到最后时,for循环内部level--都没有执行过4次,因为都break出来了。

int main()
{
    const vector<int> upper = {3, 6, 16, 5};
    const vector<int> lower = {0, 2, 5, 3};
    vector<int> index = lower;
    int level;
 
    do{
        for (auto i : index) cout<<i<<" ";
        cout<<endl;
          
        for (level = upper.size() - 1; level >= 0; level--) {
            index[level]++;
            if (index[level] >= upper[level]) 
                index[level] = lower[level];
            else 
                break;
            
        }
    } while (level >= 0);
 
    return 0;
}

方法3:

说起循环我们就应该想到递归,所以我们也可以用递归来改写这个多重循环,原理跟上面的方法差不多,只是外层循环用递归代替了。代码如下:

const vector<int> upper = {3, 6, 16, 5};
const vector<int> lower = {0, 2, 5, 3};
 
void dfs(vector<int> & nums, int level) {
 
    //do something with arr[], such as print
    for (auto n : nums) cout<<n<<" ";
    cout<<endl;
    
    bool endCondition = true;
    for (int i = 0; i <= level; ++i) {
        if (nums[i] < upper[i] - 1) {
            endCondition = false;
            break;
        }
    }
    
    if (endCondition) return;
    
    for (int i = nums.size() - 1; i >= 0; i--) {
        nums[i]++;
        if (nums[i] >= upper[i])
            nums[i] = lower[i];
        else
            break;
    }
    
    dfs(nums, level);
}
 
int main()
{
    vector<int> nums = lower;
    dfs(nums, 4);
 
    return 0;
}
调用的时候用dfs(index, 3)即可。