全排列算法

今天看剑指offer,看到里面的全排列算法,研究了一下。终于搞懂怎么个递归法了。

如果我们有一个序列:a,b, c,d。
当我们输出这四个字符的全排列时,输出的格式肯定时
4个字符:a1,a2,a3,a4。这种形式

先考察第一个字符a1的位置。
在输出全排列的时候,a1取值依次为:a1 = a或b或c或d。
好了,下面是重点了,想通了全排列的递归就很直观了
①当a1取定为a的时候,余下序列为b,c,d。
我们发现a和序列b,c,d的全排列的组合 {a,bdc全排列}。就构成了以a开头的序列的全排列。子问题就是求bcd序列的全排列了。可以发现此时子问题的规模减小了1。那么用递归就很直观了。
②好了,然后我们把a1取定为b的时候。那么问题的求解就编程了{b,adc全排列}
如此类推,直到所有a1的可能的取值下的子问题都解决了,就能得到全排列了。而且序列的初始序列并不影响全排列的过程。

好了说那么多,还是通过代码来分析好点。
代码

#include<iostream>
#include<string.h>
using namespace std;
//交换
void swap_m(char*& a,char*& b)
{
    if(*a != *b)
    {
        *a = *a ^ *b;
        *b = *a ^ *b;
        *a = *a ^ *b;
    }else if(*a == *b){
        cout<<"发生相同交换啦"<<endl;
    }
} 


void pernum(char* per,char* begin)
{
    if(per == NULL) return ;

    if(*begin == '\0')
    {
        cout<<per<<endl;
    }else{

        for(char* pCh = begin;*pCh != '\0';pCh++)
        {
        //函数刚进入的时候看这里,递归第0层
        //pch指针了当前序列:abcd的第一个a,然后不断自增
        //然后不断的和begin(此时指向了a)的值交换
        //第0层的循环遍历完了后就是把,a1的所有可能取值就列举完了

        //函数进入递归后看这里,递归第1层
        //从第0层进入到第1层后,就变成求解子问题了
        //此时pch指向了当前序列:bcd的第一个b,然后不断自增
        //特别注意,特别注意,特别注意!!!
        //第一层递归的子问题是bcd,不是acd。
        //因为for循环开始的时候pch=begin,
        //所以for循环中第一次调用swap()本质上是交换了两个一样的值。交换完自己后就进入了深一层的递归,求解bcd的子问题

        //函数进入递归第二层后,求解的就是bdc的子问题cd了
        //如此类推,直到子问题的规模为0,即begin指向的子问题序列为空了,就可以输出了。
        //要注意第i层递归的begin的指向总在,第i-1层递归的begin的指向的后一个,因为传值操作!!发生的是拷贝
            swap(pCh,begin);

            pernum(per,(begin+1));
        //这里的交换是为了把序列恢复成初始的序列
            swap(pCh,begin);
        }
     }
}

int main()
{
    char per[] ="abcd";
     pernum(per,per);
 return 0;

}

好了,我们来看看输出

这里写图片描述

可以看到,函数刚刚调用的时候,发生了4次相同交换,递归了四层,子问题的规模终于为0了。到d交换完自己后,再进一层递归,此时begin值向为’\0’了,就输出abcd了。退出一层,cd交换成dc,进入递归,c自身交换,在进入递归,输出abdc,退出,恢复的时又发生一次c自身交换,退出。后面的就不慢慢分析了。

同理数组中的全排列也是类似的,其实全排列的本质是一个DFS的过程。

#include<bits/stdc++.h> 
using namespace std;

void premu(int array[],int index,int length)
{
    if(index == length-1)
    {
        for(int i=0;i<length;i++){
            cout<<array[i]<<" ";
        }
        cout<<endl;
        return ;
    }

    for(int i=index;i<length;i++)
    {
        swap(array[i],array[index]);
        if(array[i]!=array[index]);
        premu(array,index+1,length);
        swap(array[i],array[index]);
    }
}
int main()
{
    int array[]={1,2,3,4};

        premu(array,0,sizeof(array)/sizeof(*array));

 return 0;

}
  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值