11.幂集计算[回溯递归]<一>

Posted YuRi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了11.幂集计算[回溯递归]<一>相关的知识,希望对你有一定的参考价值。

还是按照老方法,把问题最大程度的精简,现在求集合A={a,b}的幂集,只有两个元素,应该有{a,b},{a},{b},{x}四种可能。如果把这两个元素弄清楚了,其余的也都一样, 仅仅是数量增大了一些。
现在用两个数组,A是原集,B存放子集。关键是约束条件,就是边界如何界定。先这样考虑,先在B集合中放入一个元素,再放入一个元素,已经不能再放了,因为A集只有两个元素,就像是手中只有两个球,都拿出去就没有了,所以这里把约束条件定为,只要拿出去的数量>=手中的数量,就返回。
技术分享如图所示,这里用的是下标,所以第一次拿出a时,i=0,(以后类同),再放一个,i=1,企图再放一个i=2时已经触到边界,因为拿出去的数量已经最大。到此返回,打印结果,于是得到一个集合{a,b}。

接着,出现了转移。假如把第二个字符b收回(相当于没放),虽然没放东西,但是经过了一步,于是接着得到i=2,又触到边界,打印结果返回,结果得到集合{a}。
技术分享从这两个图可以看出来,这就类似于树的先序遍历,如果是先序遍历的思路,剩下的就容易理解,因为先序的返回顺序是简易的,现在i=1的情况已经穷尽,也就是左右子树都已经返回,下一步就转移到根结点,也就是i=0时的情况。

同样,由于是递归,所以把a也舍去,然后遍历根的右子树,进行i=1计算。

void f(int i,char A[],int n)
{
    char x;
    int k;
    if(i>=n)
    {
        if(B[0])
        cout<<{<<B<<}<<endl;
        else
        cout<<x<<endl;
    }
    else
    {
        x=A[i];
        k=strlen(B);
        B[k]=x;
        f(i+1,A,3);
        B[k]=0;
        f(i+1,A,3);
    }
}

i=1的计算如下:
由于舍去a,相当于集合B没有放入一个元素,长度为0,因此,进入函数f(1)时,把A集合的第二个元素,也就是b放了B[k],也就是B[0]中。
技术分享
再进入,i=2时,触到边界返回。这只是f(1)的左子树,接着进入右子树,同样,把b舍去,也即b[k]=0,(b[0]=0),进入f(2),由于集合B完全为空,所以最后打印空集‘x‘。

到这时,f(0)所调用的函数,左子树和右子树都已经返回,函数结束,打印的顺序为,ab,a,b,x。
这种回溯算法,初次接触会有些绕,不要用一大堆数据,把它简化成最简易的形式,递归调用步数少的演示一番,就容易看的明白。

这题的关键有两条,第一,划定边界,也就是什么条件下递归结束。第二,在递归过程中间,返回的时候需要处理什么事,比如这段代码中的b[k]=0,是最关键的一步,也就是舍去某些元素!

以上是关于11.幂集计算[回溯递归]<一>的主要内容,如果未能解决你的问题,请参考以下文章

Java求幂集与List的浅拷贝深拷贝问题

《程序员面试金典(第6版)》面试题 08.04. 幂集(回溯算法,位运算,C++)不断更新

素数环(递归 搜索 回溯)

递归与回溯9:子集问题-求子集

递归与回溯10:子集,有重复元素

回溯法八皇后问题(递归和非递归)