排列

1、全排列

输入一个字符串,打印出该字符串中字符的所有排列。例如输入字符串abc,则输出由字符a、b、c所能排列出来的所有字符串abc、acb、bac、bca、cab和cba。

固定第一个字符a,求后面两个字符bc的排列。当两个字符bc的排列求好之后,我们把第一个字符a和后面的b交换,得到bac,接着我们固定第一个字符b,求后面两个字符ac的排列。现在是把c放到第一位置的时候了。记住前面我们已经把原先的第一个字符a和后面的b做了交换,为了保证这次c仍然是和原先处在第一位置的a交换,我们在拿c和第一个字符交换之前,先要把b和a交换回来。在交换b和a之后,再拿c和处在第一位置的a进行交换,得到cba。我们再次固定第一个字符c,求后面两个字符b、a的排列。


void arrange(char *s,char *begin,char *end)
{
    assert(s!=NULL);
    if(begin==end)
    {
        cout<<s<<endl;
        return;
    }

    for(char *p=begin;p<=end;p++)
    {
        swap(*p,*begin);
        arrange(s,begin+1,end);
        swap(*p,*begin);
    }
}

 

2、去重全排列

      以上的全排列方案,没有考虑重复元素的情况,例如对于字符串hee,的全排列为:hee/hee/ehe/eeh/eeh/ehe,现在要求不输出重复的排列

思路:

     查看当前待排列元素,是否之前出现过,如果出现过,则不进行交换

bool repeat(char *s,char *e)
{
    bool exist=false;
    while(s!=e)
    {
        if(*s==*e)
        {
            exist=true;
        }
        s++;
    }
    return exist;
}

void arrange_norepeat(char *s ,char *begin,char *end)
{
    assert(s!=NULL);
    if(begin==end)
    {
        cout<<s<<endl;
        return;
    }

    for(char *p=begin;p<=end;p++)
    {
        if(!repeat(begin,p))
        {
            swap(*p,*begin);
            arrange_norepeat(s,begin+1,end);
            swap(*p,*begin);
        }
    }
}

 

3、字典顺序输出全排列

思路:

找出array[k]<array[k+1]的最大k值,其中0<=k<size-1,如果不存在则已经是排序的最大值

且l是所有大于a[k]的最小值

交换a[l]和a[k]

逆序

    步骤(1):

abc全排列Java abc所有排列组合输出_abc全排列Java

    步骤(2):

abc全排列Java abc所有排列组合输出_字符串_02

    步骤(3):

abc全排列Java abc所有排列组合输出_abc全排列Java_03

    步骤(4):

abc全排列Java abc所有排列组合输出_abc全排列Java_04

 

4、非递归版本全排列

Johnson  Trotter算法:

思路:

    (1)初始化一个序列 1,2,3....n 并且方向全部向左,然后输出这个序列

    (2)找到最大的 “可移动”元素 K,K和它所指方向的邻居交换,若不存在“可移动数”则退出

    (3)所有比K大的数方向全部逆序,然后输出序列

    (4)跳转到(2)

注:

    “可移动”数——一个数它的箭头所指的邻居比它小,称其为“可移动”数

abc全排列Java abc所有排列组合输出_全排列_05

 

组合

    思路:

          0-1背包问题,可以将当前元素放入,也可以不放当前元素

void combination(const char *s,vector<char>& result)
{
    if(s==NULL)
        return;
    if(*s==0)
    {
        for(int i=0;i<result.size();i++)
        {
            cout<<result[i];
        }
        cout<<endl;
        return;
    }

      //放入当前元素
    result.push_back(*s);
    combination(s+1,result);
    result.pop_back();
    
    //不放入当前元素
    combination(s+1,result);
}