对 “程序1-32”的理解

写在前面:
对递归这部分,终觉得要下些功夫,因为最初学习这部分的时候理解起来就有些抽象。机缘巧合在这本书中将递归用几个例子说得明明白白,所以决定耐心下来,类似单步执行般,将这个例子仔细梳理一遍,务求理解。

P28页用C++写的递归过程分析

#include <iostream>
#include <iterator>

using namespace std;

template <class T>
void perm(T list[], int k, int m) // list is array name, m is number of elements, k is count of elements has already been picked out to front (equals ei of ei.perm(X))
{
    if(k == m) // base component
        copy(list, list + m + 1, ostream_iterator<T>(cout, "")); // list is begin(),list+m+1 is end()
    else // recursion component
    {
        for(int i = k; i <= m; i++)
        {
            /* 需要将list[k:m]中的每一个元素分别提到最前面,并与余下的 “不含此元素的集合的所有组合” 进行组合;
             * 因要使用递归,元素数需要减少一个,从list[k]这个元素开始,将它提出,余下的再组合;
             *
             *  list[k]这个位置很特殊,它是下一级递归所不包含的位置,所以提取元素出来时就可以放在这个位置,
             *      那能不能放在list[k-1]或list[k-x]只要不超出list的边界即可呢?不能,因为list[k]这个位置的元素,
             *		是要参与到当前perm()层的排列组合中的,而list[k-1]却不参与
             *
             * 于是就出现了这样的效果,list[k]这个位置,分别被list[k+1],list[k+2],……,list[m]所取代,
             * 		在执行完中间的perm()后,在随后的第2个swap()中又被复原,
             *      也就是说,假设初始list[k]的值为AA,则过程是,AA与list[k+1]交换,然后换回,AA再与list[k+2]交换,
             *		然后换回,依次类推,直到AA与list[m]交换并再换回;
             *
             * 所以,第1个swap()的任务,是完成对各元素的“提取并放在首位”,第2个swap()的任务,是将提出的元素放回到集合中
             */
            swap(list[k], list[i]);
            perm(list, k + 1, m);
            // cout << endl;
            swap(list[k], list[i]);
        }
    }
}

int main()
{
    char arr[5] {'a', 'b', 'c', 'd', 'e'};
    perm<char>(arr, 0, 4);
    system("pause");
    return 0;
}

分析for的过程:

  • 进入最外层for之后,第1个swap()将list[k]与list[i]交换,随着i的增加,相当于逐个将list[k:m]中的元素提出来放在最前面

  • 随后进入下一级perm(),注意,这里不是马上执行第2个swap(),而是需要执行完这个perm()

  • 最新进入的perm()中,又要涉及,对减少一个元素后的集合,分别提取出元素放在最前面的过程

  • 可以发现,每层for中第2个swap()要等到它上面一句的perm()全部执行完毕后,才会被执行,于是就有了书中P28图1-3中的顺序

对书中图1-3进行的过程分析:

必须要理解的一个点:这个permutations(),就是通过修改list数组中元素的位置,来到达表达每个不同组合的,每个组合都对应着一个全新的list,而这个变化后的list就是要在base component中遍历显示的。要区别于最初始状态的list数组。

下面分析过程:(第1层指最外层)

  • 1-2: 第1层for中第1个swap()将a与a调换 (注意,此时list[k]==a),list此时为{a,b,c},进入下一层perm()

  • 2-3: 第2层for中第1个swap()将b与b调换 (注意,此时list[k+1]==b),list此时为{a,b,c},进入下一层perm(),第3层perm()发现k==m,所以直接执行base component部分,遍历显示每个元素

  • 3-2: 第2层for中第2个swap()将b与b恢复调换,list此时为{a,b,c}

  • 2-4: 第2层for中第1个swap()将c与b调换,list此时为{a,c,b},进入下一层perm(),第3层perm()发现k==m,所以直接执行base component部分,遍历显示每个元素

  • 4-2: 第2层for中第2个swap()将c与b恢复调换,list此时为{a,b,c}

  • 2-1: 第1层for中第2个swap()将a与a恢复调换,list此时为{a,b,c}

  • 截止此时,第1层for中i=0的情况,已经完成

  • %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%(分隔线)

  • 下面进行,第1层for中i=1的情况,即list[i]=b的情况

  • 1-5: 第1层for中第1个swap()将b与a调换 (注意,此时list[k]==a,list[i]==b),list此时为{b,a,c},进入下一层perm()

  • 5-6: 第2层for中第1个swap()将a与a调换,相当于没变,list变为{b,a,c},进入下一层perm(),第3层perm()发现k==m,所以直接执行base component部分,遍历显示每个元素

  • 6-5: 第2层for中第2个swap()将a与a恢复调换,相当于没变,list仍为{b,a,c}

  • 5-7: 第2层for中第1个swap()将a与c调换,list变为{b,c,a},进入下一层perm(),第3层perm()发现k==m,所以直接执行base component部分,遍历显示每个元素

  • 7-5: 第2层for中第2个swap()将a与c恢复调换,list恢复为{b,a,c}

  • 5-1: 第1层for中第2个swap()将b与a恢复调换,list此时为{a,b,c}

  • 截止此时,最外层for的i=1的情况完成

  • %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%(分隔线)

  • 下面进行,第1层for中i=2的情况,即list[i]=c的情况

  • 1-8: 第1层for中第1个swap()将a与c调换 (注意,此时list[k]==a,list[i]==c),list此时为{c,b,a},进入下一层perm()

  • 8-9: 第2层for中第1个swap()将b与b调换,list仍为{c,b,a},进入下一层perm(),第3层perm()发现k==m,所以直接执行base component部分,遍历显示每个元素

  • 9-8: 第2层for中第2个swap()将b与b恢复调换,lise仍为{c,b,a}

  • 9-10: 第2层for中第1个swap()将b与a调换,list变为{c,a,b},进入下一层perm(),第3层perm()发现k==m,所以直接执行base component部分,遍历显示每个元素

  • 10-9: 第2层for中第2个swap()将b与a恢复调换,list变为{c,b,a}

  • 9-1: 第1层for中第2个swap()将a与c恢复调换,list此时为{a,b,c}

  • 截止目前,所有层的perm()全部得到了完整的执行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值