C++代码示例:排列数简单生成工具

前言

C++代码示例:排列数简单生成工具。


代码仓库


内容

  • 简单地生成排列数
  • 有详细的步骤解析

代码(有详细注释)

cpermutation.cpp

#include <vector>
#include <iostream>
#include <unordered_set>

using std::cout;
using std::endl;
using std::unordered_set;
using std::vector;

class CPermutation
{
public:
    CPermutation(const int &n, const int &m) : n(n), m(m), flags(0), curPerm(0), permCount(0) {}

    bool next()
    {
        // 第一次进入初始化当前排列,如m=3,this->curPerm为012,从012开始遍历
        if (this->curPerm.empty() == true)
        {
            for (int i = 0; i < this->m; ++i)
            {
                this->curPerm.push_back(i);
                this->flags.insert(i); // 记录哈希
            }

            ++this->permCount;
            return true;
        }

        // 1. 首先,确定某个位置是否已经达到了当前意义下的“最大值”—“当前” 的意义:前面的位置数值固定之后
        // 求当前位置应当的最大值
        // 注意理解:如014,索引2位置可取23,最大值为3;索引1位置可取234,最大值为4;索引0位置可取1234,最大值为4,左边位置的取值范围要包括右边的位置
        // 从后往前大到小遍历,如果这个数不在哈希表中就是应该的最大值

        // 2. 然后,从后向前寻找第一个不是当前最大值的位置
        // 修正:小于当前应当最大值的位置
        // 如014,则求得的位置是索引1
        int curPos = this->m - 1;
        int curMax = 0;
        while (curPos >= 0)
        {
            // 1.
            for (int i = this->n - 1; i >= 0; --i)
            {
                if (flags.find(i) == flags.end())
                {
                    curMax = i;
                    break; // 找到就退出for
                }
            }

            // 2.
            if (this->curPerm.at(curPos) >= curMax) // 未找到位置移动
            {
                flags.erase(this->curPerm.at(curPos)); // 哈希表减少
                --curPos;
            }
            else // 找到位置退出while
            {
                break;
            }
        }

        if (curPos < 0) // 没找到位置搜完排列
        {
            return false;
        }

        // 3.这个位置上的数值最小限度地增加—“最小限度”,前面的数值排除之后,取剩余的、大于该值的最小值
        // 从前往后小到大遍历,如果这个数不在哈希表中就是应该变换的值
        // 注意:此时可能处理左边的位置,哈希表还是删减状态以让左边位置可以取到右边的值
        // 如014,遍历012到2最小且不在哈希表,将索引1位置元素1换成2,该位置后续可以取到右边的值4,右边的值相应的会减小不再是4保证不重复
        for (int i = this->curPerm.at(curPos); i < this->n; ++i)
        {
            if (flags.find(i) == flags.end())
            {
                this->flags.erase(this->curPerm.at(curPos)); // 哈希删除
                this->curPerm.at(curPos) = i;
                this->flags.insert(i); // 哈希插入
                break;
            }
        }

        // 归位哈希表。因为右边的数要填充了
        // 如034,位置1可以取到4,但此时哈希表是没有4的,将3变为4后,需要加入哈希表,右边的值填充时不能再是4,不是044而是041
        for (const int p : this->curPerm)
        {
            flags.insert(p);
        }

        // 4. 该位置确定后,后面的位置依次取剩余数值的最小几个
        // 对右边的所有位置
        // 从前往后小到大遍历,如果这个数不在哈希表中就是应该变换的值
        // 如024,遍历01到1最小且不在哈希表,
        for (int i = curPos + 1; i < this->m; ++i)
        {
            for (int j = 0; j < this->n; ++j)
            {
                if (flags.find(j) == flags.end())
                {
                    this->flags.erase(this->curPerm.at(i)); // 哈希删除
                    this->curPerm.at(i) = j;
                    this->flags.insert(j); // 哈希插入
                    break;
                }
            }
        }

        ++this->permCount;
        return true;
    }

    inline void printCurPerm()
    {
        for (const int p : this->curPerm)
        {
            cout << p << " ";
        }
        cout << endl;
    }

    inline void printPermCount()
    {
        cout << this->permCount << endl;
    }

private:
    const int n; // 从n个取m个
    const int m;

    vector<int> curPerm;      // 当前排列,存储排列的索引
    unordered_set<int> flags; // 哈希标志,标记已在排列中的索引
    int permCount;            // 排列数的数量
};

int main()
{
    const int n = 5;
    const int m = 3;
    CPermutation perm(n, m);

    while (perm.next())
    {
        perm.printCurPerm();
    }
    perm.printPermCount();

    return 0;
}

编译和运行命令

g++ -o cpermutation cpermutation.cpp
./cpermutation.exe

结果

在这里插入图片描述
在这里插入图片描述


总结

C++代码示例:排列数简单生成工具。


参考资料

  • 学校《高级算法设计与分析》课程课件的算法思路

作者的话

  • 感谢参考资料的作者/博主
  • 作者:夜悊
  • 版权所有,转载请注明出处,谢谢~
  • 如果文章对你有帮助,请点个赞或加个粉丝吧,你的支持就是作者的动力~
  • 文章在描述时有疑惑的地方,请留言,定会一一耐心讨论、解答
  • 文章在认识上有错误的地方, 敬请批评指正
  • 望读者们都能有所收获

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值