全排列的递归和非递归实现

2 篇文章 0 订阅

全排列问题

1.全排列的递归实现

全排列就是从第一个数字起每个数分别与它后面的数字交换

#include <iostream>
#include <vector>
#include <string>
using namespace std;

//递归实现
void allRange(string s, int k, int m)
{
    if (k == m){
        static int s_i = 1;
        cout << "第" << s_i++ << "个排列是:" << s << endl;
    }
    else{
        for (int i = k; i <= m; i++){
            swap(s[i], s[k]);
            allRange(s, k + 1, m);
            swap(s[i], s[k]);
        }
    }
}

int main() {
    string s = "abc";
    allRange(s, 0, s.length() - 1);
    return 0;
}

运行结果:

第1个排列是:abc
第2个排列是:acb
第3个排列是:bac
第4个排列是:bca
第5个排列是:cba
第6个排列是:cab

但是这没有考虑到序列中重复的情况

第1个排列是:abb
第2个排列是:abb  //重复
第3个排列是:bab
第4个排列是:bba
第5个排列是:bba  //重复
第6个排列是:bab  //重复
2.去重的全排列的递归实现

去重的全排列就是从第一个数字起每个数分别与它后面非重复出现的数字交换

#include <iostream>
#include <vector>
#include <string>
using namespace std;

bool needSwap(string s, int k, int m) {
    for (; k < m; k++)
        if (s[k] == s[m])
            return false;
    return true;
}

//递归实现
void allRange(string s, int k, int m)
{
    if (k == m){
        static int s_i = 1;
        cout << "第" << s_i++ << "个排列是:" << s << endl;
    }
    else{
        for (int i = k; i <= m; i++){
            if (!needSwap(s, k, i))
                continue;
            swap(s[i], s[k]);
            allRange(s, k + 1, m);
            swap(s[i], s[k]);
        }
    }
}

int main() {
    string s = "abb";
    allRange(s, 0, s.length() - 1);
    return 0;
}

运行结果:

第1个排列是:abb
第2个排列是:bab
第3个排列是:bba
3.全排列的非递归实现

要考虑全排列的非递归实现,先来考虑如何计算字符串的下一个排列。如"1234"的下一个排列就是"1243"。只要对字符串反复求出下一个排列,全排列的也就迎刃而解了。

如何计算字符串的下一个排列了?来考虑"926520"这个字符串,我们从后向前找第一双相邻的递增数字,“20”、"52"都是非递增的,“26 “即满足要求,称前一个数字2为替换数,替换数的下标称为替换点,再从后面找一个比替换数大的最小数(这个数必然存在),0、2都不行,5可以,将5和2交换得到"956220”,然后再将替换点后的字符串"6220"颠倒即得到"950226”。

对于像"4321"这种已经是最“大”的排列,采用STL中的处理方法,将字符串整个颠倒得到最“小”的排列"1234"并返回false。

#include <iostream>
#include <vector>
#include <string>
using namespace std;

bool nextArrangement(string &s) {
    int index = -1;
    for (int i = s.length() - 1; i > 0; i--) {
        if (s[i] > s[i - 1]) {
            index = i - 1;
            break;
        }
    }
    if (index == -1)
        return false;
    for (int i = s.length() - 1; i > index; i--) {
        if (s[i] > s[index]) {
            swap(s[i], s[index]);//交换
            reverse(s.begin() + index + 1, s.end());
            break;
        }
    }
    return true;
}

int main() {
    string s = "abc";
    int count = 1;
    do {
        cout << "第" << count++ << "个排列是:" << s << endl;
    } while (nextArrangement(s));
    return 0;
}

运行结果:

第1个排列是:abc
第2个排列是:acb
第3个排列是:bac
第4个排列是:bca
第5个排列是:cab
第6个排列是:cba
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值