当前位置: 首页 > news >正文

C++中的排列组合算法详解

引言:排列组合的重要性

排列组合是计算机科学中的基础算法,广泛应用于密码学、数据分析、游戏开发等领域。在C++中,我们通常使用深度优先搜索(DFS)和回溯法来实现排列组合算法。本文将详细解析全排列、组合及其变种的实现原理和代码。

一、全排列算法

1.基本概念:

全排列是指从n个不同元素中取出n个元素,按照一定的顺序排列的所有可能情况。例如,元素{1,2,3}的全排列有6种:123、132、213、231、312、321。

2.算法实现:

#include <iostream>
using namespace std;
const int N = 101; // 数据范围
int a[N];          // 存储待排列的数
int show[N];       // 存储已排列的数
int n;             // 数的个数
bool check[N];     // 标记数字是否已使用void dfs(int showid) {if (showid == n + 1) { // 完成一种排列for (int i = 1; i <= n; i++)cout << show[i] << " ";cout << endl;return;}for (int i = 1; i <= n; i++) {if (!check[i]) {         // 选择未使用的数字check[i] = true;     // 标记为已使用show[showid] = a[i]; // 放入当前位置dfs(showid + 1);     // 递归处理下一个位置check[i] = false;    // 回溯,取消标记}}
}int main() {cin >> n;for (int i = 1; i <= n; i++)cin >> a[i];dfs(1); // 从第一个位置开始return 0;
}

3.算法解析:

  1. DFS框架:使用递归实现深度优先搜索

  2. 标记数组check[]记录数字使用状态

  3. 回溯机制:递归返回后撤销选择

  4. 时间复杂度:O(n!) - 阶乘复杂度

二、有重复数字的全排列

1.问题特点:

当输入数组包含重复数字时,基本全排列算法会产生重复结果。例如,输入{1,1,2}会产生3种排列而不是2种。

2.优化实现:

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 101;
int a[N];
int show[N];
int n;
bool check[N];void dfs(int showid) {if (showid == n + 1) {for (int i = 1; i <= n; i++)cout << show[i] << " ";cout << endl;return;}for (int i = 1; i <= n; i++) {// 关键去重逻辑if (i > 1 && a[i] == a[i - 1] && !check[i - 1])continue;if (!check[i]) {check[i] = true;show[showid] = a[i];dfs(showid + 1);check[i] = false;}}
}int main() {cin >> n;for (int i = 1; i <= n; i++)cin >> a[i];sort(a + 1, a + 1 + n); // 必须先排序dfs(1);return 0;
}

3.去重关键:

  1. 排序预处理:确保相同元素相邻

  2. 跳过条件if (i>1 && a[i]==a[i-1] && !check[i-1])

    • 当前元素与前一个相同

    • 前一个元素未被使用(说明是新层级)

  3. 时间复杂度:最坏情况仍为O(n!)

三、组合算法

1.基本概念:

组合是指从n个不同元素中取出r个元素,不考虑顺序。例如,从{1,2,3,4}中取2个元素的组合有6种:{1,2}、{1,3}、{1,4}、{2,3}、{2,4}、{3,4}。

2.算法实现:

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 101;
int a[N];
int show[N];
bool check[N];
int n, r; // n个数中取r个void dfs(int showid) {if (showid == r + 1) { // 选够r个数for (int i = 1; i <= r; i++)cout << show[i] << " ";cout << endl;return;}for (int i = 1; i <= n; i++) {// 避免重复组合的关键条件if (!check[i] && a[i] > show[showid - 1]) {check[i] = true;show[showid] = a[i];dfs(showid + 1);check[i] = false;}}
}int main() {cin >> n >> r;for (int i = 1; i <= n; i++)cin >> a[i];sort(a + 1, a + 1 + n); // 排序确保升序show[0] = -1; // 初始值小于所有可能数dfs(1);return 0;
}

3.组合关键点:

  1. 避免重复:只选择比前一个元素大的数

  2. 排序预处理:确保数组有序

  3. 初始值设置show[0] = -1作为哨兵值

  4. 时间复杂度:O(C(n,r)) - 组合数复杂度

四、带重复数字的组合

1.问题特点:

当输入包含重复数字时,需要避免生成重复的组合结果。

2.优化实现:

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 101;
int a[N];
int show[N];
bool check[N];
int n, r;void dfs(int showid) {if (showid == r + 1) {for (int i = 1; i <= r; i++)cout << show[i] << " ";cout << endl;return;}for (int i = 1; i <= n; i++) {// 去重逻辑if (i > 1 && a[i] == a[i - 1] && !check[i - 1])continue;// 组合条件if (!check[i] && a[i] >= show[showid - 1]) {check[i] = true;show[showid] = a[i];dfs(showid + 1);check[i] = false;}}
}int main() {cin >> n >> r;for (int i = 1; i <= n; i++)cin >> a[i];sort(a + 1, a + 1 + n);show[0] = -1; // 初始哨兵值dfs(1);return 0;
}

3.去重关键:

  1. 排序预处理:确保相同元素相邻

  2. 跳过条件if (i>1 && a[i]==a[i-1] && !check[i-1])

  3. 组合条件a[i] >= show[showid-1](允许相等)

五、组合的高效实现

1.优化思路:

使用起始索引避免重复选择和标记数组

#include<iostream>
#define N 30
using namespace std;
int n, r;
int a[N]; // 存储当前组合
int vis[N]; // 标记数组void dfs(int step) {if (step == r + 1) { // 完成组合for (int i = 1; i <= r; i++)cout << a[i] << " ";cout << endl;return;}// 从上一个数开始选择for (int i = a[step - 1]; i <= n; i++) {if (!vis[i]) {a[step] = i;vis[i] = 1;dfs(step + 1);vis[i] = 0;}}
}int main() {cin >> n >> r;a[0] = 1; // 初始化起始值dfs(1);return 0;
}

2.算法优势:

  1. 避免重复:通过i = a[step-1]确保顺序选择

  2. 减少循环:不需要遍历所有数字

  3. 空间优化:不需要存储原始数组

六、排列组合算法对比

算法类型特点时间复杂度空间复杂度关键技巧
全排列所有元素排列O(n!)O(n)标记数组、回溯
有重复全排列处理重复元素O(n!)O(n)排序、跳过条件
组合选择r个元素O(C(n,r))O(n)排序、升序选择
有重复组合处理重复元素O(C(n,r))O(n)排序、双重条件
高效组合优化选择过程O(C(n,r))O(n)起始索引

相关文章:

  • 学习使用YOLO的predict函数使用
  • Go 语言 range 关键字全面解析
  • 算法题(164):贴海报
  • Mysql 插入中文乱码
  • MS2691 全频段、多模导航、射频低噪声放大器芯片,应用于导航仪 双频测量仪
  • STM32标准库-输入捕获
  • 在 Windows 11 或 10 上删除、创建和格式化分区
  • 力扣刷题(第五十天)
  • 蓝桥杯单片机之通过实现同一个按键的短按与长按功能
  • Java-IO流之序列化与反序列化详解
  • exec进程替换函数族
  • Docker基础(二)
  • 2.3 VS2019 简单使用
  • 跟我学c++中级篇——C++14中的透明操作符
  • 图片批量格式转换工具
  • 视频字幕质量评估的大规模细粒度基准
  • Spring IoC 模块设计文档
  • ZephyrOS 嵌入式开发Black Pill V1.2之Debug调试器
  • 力扣hot100---152.乘积最大子数组
  • leetcode 3170. 删除星号以后字典序最小的字符串 中等
  • 无锡网站公司电话/官方百度app下载
  • wordpress开发工作流/比较好的网络优化公司
  • 用html5做的网站的原代码/青岛网站关键词优化公司
  • 报社网站开发做什么/网络推广推广
  • 免费淘宝网站建设/seo优化师
  • 如何抄袭网站/百度发布