全排列

从n个不同元素中任取m(m≤n)个元素,按照一定的顺序排列起来,叫做从n个不同元素中取出m个元素的一个排列。当m=n时所有的排列情况叫全排列。

一:全排列的递归实现

拿123来示例递归算法流程。

123      1和1换 

132      1和1换完,接下来把 23当成一个序列,进行2和2换->123 再把3当成一个序列,这时要换的数就是序列的最后一位,输出结果(123)。再回到23序列,2和3换,反复,得到结果。

213      1和2换

231      1和2换完,接下来把 13当成一个序列,进行1和1换->213再把3当成一个序列,这时要换的数就是序列的最后一位,输出结果(213)。再回到13序列,1和3换,反复,得到结果。

321      1和3换

312      同上

实际上全排列就是从第一个数开始,每个数字分别与后面数字进行交换,通过上面的步骤,当交换的那个数,换到了最后一位,输出,实现递归。

代码实现:

#include<vector>
#include<iostream>
#include<algorithm>
#include<iterator>
using namespace std;
void perm(vector<int> &vec,int k,int m)
{
    if (k == m){                   //就是说k从本次perm开始的那个位置换到尾了  即这个子序列只有一个字符时。递归最终实现。
        copy(vec.begin(), vec.end(), ostream_iterator<int>(cout, " "));
        cout << endl;
        return;
    }
    for (int i = k; i <= m; ++i){
        swap(vec[k], vec[i]);
        perm(vec,k+1,m);          //比如 123  1往后换->得到213 321   对于213 再对13序列执行perm
        swap(vec[k], vec[i]);     //将数组回溯到原来的状态
    }
}

二:去掉重复的全排列的递归实现

去重复的全排列就是从第一个数字起,每个数字分别与它后面非重复出现的数字进行交换。比如122  1和第一个2交换过,就不和第二个2交换了。

在交换时,加一个判断函数即可

bool isSwap(vector<int>&vec, int kbeg, int iend)    //要判断从k开始到i 就是说换到现在,该换i了,要看之前出现过i没。
{
    for (int i = kbeg; i != iend; ++i)              // 122  1和第一个2换 先看之前1和2不相等 可以换
        if (vec[i] == vec[iend])return false;       // 122  1和第二个2换 2之前的2==2,已经换过了,不换了。
    return true;
}
void perm(vector<int> &vec,int k,int m)
{
    if (k == m){                   //就是说k从本次perm开始的那个位置换到尾了  即这个子序列只有一个字符时。递归最终实现。
        copy(vec.begin(), vec.end(), ostream_iterator<int>(cout, " "));
        cout << endl;
        return;
    }
    for (int i = k; i <= m; ++i){
        if (isSwap(vec, k, i)){
            swap(vec[k], vec[i]);
            perm(vec, k + 1, m);          //比如 123  1往后换->得到213 321   对于213 再对13序列执行perm
            swap(vec[k], vec[i]);     //将数组回溯到原来的状态。
        }
    }
}

 三:全排列—非递归实现

非递归实现,就要对每一个序列求出一个后继,那么自然而然要有一个终点,即什么时候,序列没有后继。

所以要有序列的大小,最大或最小就没有后继了。

对于升序我们求后继,就是不断的求下一个比该序列大的最小的序列。当序列是最大的序列时,不再有后继。

从后往前搜索,找到一个极大值点(top) vec[top-1] < vec[top] ,

要使得下个数大于前一个数,找一个大于vec[top-1]的数和vec[top-1] 交换,但要使得它最小,取其中最小的但大于num[top-1]的数。

交换之后,top以及其后面的数字还是单调递减的,将其位置对调,得到最小的数。

以 1 2 4 6 5 3 为例, 从后往前找到6 为一个极大值,考虑处理4 和后面的6 5 3 三个数字,大于4 的最小数字为5 ,对换得到5 和 6 4 3,

然后由于后面的6 4 3 还是单调递减的, 将其颠倒得到3 4 6 ,即 1 2 5 3 4 6 为 1 2 4 6 5 3的后继 。

#include<vector>
#include<iostream>
#include<algorithm>
#include<iterator>
using namespace std;
bool getNext(vector<int>&vec)
{
    auto end = vec.size() - 1;
    size_t top;
    while (end != 0){
        if (vec[end - 1] < vec[end]){
            top = end;         //得到峰值
            break;
        }
        --end;
    }
    if (end == 0)
        return false; //如果是降序排列的 654321 那么没有后继

    size_t change = top;                //要和top-1交换的数的下标
    for (auto i = top; i != vec.size(); ++i){
        if (vec[i]>vec[top - 1])         //因为top后面都是递减的,所以最后一个比top-1大的就是最小的
            change = i;
    }
    swap(vec[top - 1], vec[change]);      //找到以后进行交换
    reverse(vec.begin() + top, vec.end());//将top往后的降序改为升序
    return true;
}



#include<iostream>
#include<vector>
#include<algorithm>
#include<iterator>
#include<fstream>
using namespace std;
void perm(vector<int> &vec, int k, int m);
bool getNext(vector<int>&vec);
int main()
{
    ofstream out("all_order");
    vector<int>vec = { 1,2,3,4,5,6};
    copy(vec.begin(), vec.end(), ostream_iterator<int>(out, " "));
    out << endl;
    while (getNext(vec)){
        copy(vec.begin(), vec.end(), ostream_iterator<int>(out, " "));
        out<< endl;
    }
    system("pause");
    return 0;
}

 

posted on 2016-11-30 16:23  ToBeAprogrammer  阅读(213)  评论(0)    收藏  举报

导航