C语言全排列递归算法理解,C#算法之全排列递归算法实例讲解
排列:从n个元素中任取m个元素,并按照一定的顺序进行排列,称为排列;
全排列:当n==m时,称为全排列;
比如:集合{ 1,2,3}的全排列为:
{ 1 2 3}
{ 1 3 2 }
{ 2 1 3 }
{ 2 3 1 }
{ 3 2 1 }
{ 3 1 2 }
我们可以将这个排列问题画成图形表示,即排列枚举树,比如下图为{1,2,3}的排列枚举树,此树和我们这里介绍的算法完全一致;
算法思路:
(1)n个元素的全排列=(n-1个元素的全排列)+(另一个元素作为前缀);
(2)出口:如果只有一个元素的全排列,则说明已经排完,则输出数组;
(3)不断将每个元素放作第一个元素,然后将这个元素作为前缀,并将其余元素继续全排列,等到出口,出口出去后还需要还原数组;
这里先把集合中的元素理解为不会出现重复了,那么实现的方法(C++)如下:
成员管理,互评,文件共享,事务通知
#include
using namespace std;
int sum = 0;//记录有多少种组合
void Swap(char str[], int a, int b)
{
char temp = str[a];
str[a] = str[b];
str[b] = temp;
}
void Perm(char str[], int begin, int end)
{
if (begin == end)
{
for (int i = 0; i <= end; i++)
{
cout << str[i];
}
cout << endl;
sum++;
return;
}
else
{
for (int j = begin; j <= end; j++)
{
Swap(str, begin, j);
Perm(str, begin + 1, end);
Swap(str, j, begin);
}
}
}
int main()
{
int n;
char c[16];
char tmp;
cin >> n;
tmp = getchar(); // 接受回车
if (1 <= n && n <= 15) {
for (int i = 0; i < n; i++) {
c[i] = getchar();
}
Perm(c, 0, n - 1);
}
cout << sum;
cout << endl;
return 0;
}
实现后效果如下图:
有重复元素的排列问题
然后现在的题目要求是排列中的元素是包含相同元素的,给定n以及待排的n个可能重复的元素。计算输出n个元素的所有不同排列,因此上面那个算法显然还是不够好,因为相同的元素都当成不同的元素,因此有了重复的排列在里面
去掉重复符号的全排列:在交换之前可以先判断两个符号是否相同,不相同才交换,这个时候需要一个判断符号是否相同的函数。也就是下面的IsSwap();
对122,第一个数1与第二个数2交换得到212,然后考虑第一个数1与第三个数2交换,此时由于第三个数等于第二个数,所以第一个数不再与第三个数交换。再考虑212,它的第二个数与第三个数交换可以得到解决221。
去掉重复的规则:去重的全排列就是从第一个数字起每个数分别与它后面非重复出现的数字交换。
#include
using namespace std;
int sum=0;//记录有多少种组合
void Swap(char str[], int a, int b)
{
char temp = str[a];
str[a] = str[b];
str[b] = temp;
}
bool IsSwap(char *pchar, int nBegin, int nEnd)
{
for (int i = nBegin; i < nEnd; i++)
if (pchar[i] == pchar[nEnd])
return false;
return true;
}
void Perm(char str[], int begin, int end)
{
if (begin==end)
{
for (int i = 0; i <= end; i++)
{
cout << str[i];
}
cout << endl;
sum++;
return;
}
else
{
for (int j = begin; j <= end; j++)
{
if (IsSwap(str, begin, j))
{
Swap(str, begin, j);
Perm(str, begin + 1, end);
Swap(str, j, begin);
}
}
}
}
int main()
{
int n;
char c[16];
char tmp;
cin >> n;
tmp = getchar(); // 接受回车
if (1 <= n && n <= 15) {
for (int i = 0; i < n; i++) {
c[i] = getchar();
}
Perm(c, 0, n - 1);
}
cout << sum;
cout << endl;
return 0;
}
非递归的实现
实现思路:
要考虑全排列的非递归实现,先来考虑如何计算字符串的下一个排列。如"1234"的下一个排列就是"1243"。只要对字符串反复求出下一个排列,全排列的也就迎刃而解了。
如何计算字符串的下一个排列了?来考虑"926520"这个字符串,我们从后向前找第一双相邻的递增数字,"20"、"52"都是非递增的,"26 "即满足要求,称前一个数字2为替换数,替换数的下标称为替换点,再从后面找一个比替换数大的最小数(这个数必然存在),0、2都不行,5可以,将5和2交换得到"956220",然后再将替换点后的字符串"6220"颠倒即得到"950226"。
对于像"4321"这种已经是最“大”的排列,采用STL中的处理方法,将字符串整个颠倒得到最“小”的排列"1234"并返回false。
//全排列的非递归实现
#include
using namespace std;
void Swap(char *a, char *b)
{
char t = *a;
*a = *b;
*b = t;
}
//反转区间
void Reverse(char *a, char *b)
{
while (a < b)
Swap(a++, b--);
}
//下一个排列
bool Next_permutation(char a[])
{
char *pEnd = a + strlen(a);
if (a == pEnd)
return false;
char *p, *q, *pFind;
pEnd--;
p = pEnd;
while (p != a)
{
q = p;
--p;
if (*p < *q) //找降序的相邻2数,前一个数即替换数
{
//从后向前找比替换点大的第一个数
pFind = pEnd;
while (*pFind <= *p)
--pFind;
//替换
Swap(pFind, p);
//替换点后的数全部反转
Reverse(q, pEnd);
return true;
}
}
Reverse(p, pEnd);//如果没有下一个排列,全部反转后返回true
return false;
}
int QsortCmp(const void *pa, const void *pb)
{
return *(char*)pa - *(char*)pb;
}
int main()
{
int sum = 0;
char szTextStr[16];
cin >> szTextStr;
char tmp = getchar(); // 接受回车
//加上排序
qsort(szTextStr, strlen(szTextStr), sizeof(szTextStr[0]), QsortCmp);
int i = 1;
cout << endl << endl << endl;
do{
cout<
sum++;
} while (Next_permutation(szTextStr));
cout << sum<
return 0;
}
总结:
排列在笔试面试中很热门,在百度和迅雷的校园招聘以及程序员和软件设计师的考试中都考到了,因此了解全排列算法对我们都很有好处。也是算法的一个基本思想。递归算法的思路比较直,而非递归的就比较难去想到使用这种方法来实现。
1.全排列就是从第一个数字起每个数分别与它后面的数字交换。
2.去重的全排列就是从第一个数字起每个数分别与它后面非重复出现的数字交换。
3.全排列的非递归就是由后向前找替换数和替换点,然后由后向前找第一个比替换数大的数与替换数交换,最后颠倒替换点后的所有数据。
C语言全排列递归算法理解,C#算法之全排列递归算法实例讲解相关推荐
- C语言:深入理解排序算法
目录 1. 使用qsort实现快速排序 2. 模拟实现qsort 使用qsort实现快速排序 C语言中比较常见的排序算法有两种:一是冒泡排序法,这种方法在之前的文章中有详细介绍过,这里就不做过多赘述. ...
- Algorithm:C++语言实现之字符串相关算法(字符串的循环左移、字符串的全排列、带有同个字符的全排列、串匹配问题的BF算法和KMP算法)
Algorithm:C++语言实现之字符串相关算法(字符串的循环左移.字符串的全排列.带有同个字符的全排列.串匹配问题的BF算法和KMP算法) 目录 一.字符串的算法 1.字符串的循环左移 2.字符串 ...
- 全排列的java算法_全排列算法原理和实现
评论 # re: 全排列算法原理和实现 回复 更多评论 #include #include #define CHESSNUM 9 using namespace std; /*********** ...
- 求n的阶乘的算法框图_递归算法是一种直接或者间接调用自身函数或者方法的算法...
http://blog.csdn.NET/wangjinyu501/article/details/8248492 原版 一.基本概念 递归算法是一种直接或者间接调用自身函数或者方法的算法.Java递 ...
- xgboost 正则项_深入理解Boosting算法(4)-XGBoost
导语:记得第一次使用XGBoost的时候还是2016年,那会的XGBoost工具还不完善,深度模型还没有怎么样火起来,大家用的最多的还是Sklearn里面的GBDT,或者R语言的GBM.后来在工作中也 ...
- 从零实现来理解机器学习算法:书籍推荐及障碍的克服
从零实现来理解机器学习算法:书籍推荐及障碍的克服 发表于2015-09-10 16:16| 1261次阅读| 来源Machine Learning Mastery| 2 条评论| 作者Jason ...
- 一步一步理解Paxos算法
一步一步理解Paxos算法 作者:jw (360电商技术组) 背景 Paxos算法是Lamport于1990年提出的一种基于消息传递的一致性算法.由于算法难以理解起初并没有引起人们的重视,使Lampo ...
- 从头到尾彻底理解傅里叶变换算法(上)
从头到尾彻底理解傅里叶变换算法(上) 前言 第一部分. DFT 第一章.傅立叶变换的由来 第二章.实数形式离散傅立叶变换(Real DFT) 从头到尾彻底理解傅里叶变换算法.下 第三章.复数 第四章 ...
- 二分查找算法举例说明C语言,C语言快速排序与二分查找算法示例
本文实例讲述了C语言二分排序与查找算法.分享给大家供大家参考,具体如下: 题目:首先产生随机数,再进行快速排序,再进行二分查找. 实现代码: #include #include #include vo ...
最新文章
- VTK:图像拉普拉斯算子用法实战
- python入门之前面内容拾遗
- Oracle Hint(提示)与常用方法
- [vscode] 禁止 pylance 插件自动添加 import
- 第 13 章 MEncoder的基础用法
- linux socket编程:简易客户端与服务端
- C是一个结构化语言它的重点在于算法和数据结构
- cs架构自动化测试工具选型
- 苹果手机升级13无法开机_苹果手机升级系统后无法开机怎么办?快拿出你的小本本来做笔记吧...
- 戏精,程序员的桌面画风竟然是酱紫的!
- Notes弹窗查询异构系统数据库解决方案
- SSD硬盘,先不要用在Server上
- 腾讯会议:资源加载失败请重新安装程序解决方案
- ARTS Week 32
- B站直播,哔哩哔哩虚拟主播数据如何分析?
- 南开大学统计与数据科学院夏令营
- 3D点云基础知识(一)-初始入门-知乎整理(一)
- Python 高级可视化库Plotly express使用教程
- 基于Java实现的潜艇大战游戏
- 武汉大学计算机2017博士录取,通知公告 | 2017年博士拟录取名单公示及相关说明...
热门文章
- 漫画:程序员和产品经理撕得真是太太太太厉害了
- 【快讯】JeecgBoot低代码平台,成功入选2021科创中国·开源创新榜
- 前端基础:初步认识Chrome调试面板,学会简单的代码调试,必会!
- Jeecg-Boot 2.0.0 版本发布,基于Springboot+Vue 前后端分离快速开发平台
- 实战-Ueditor扩展二次开发
- JEECG 开源平台全视频和文档
- 一个SQL更新多条字段
- 架构设计:数据服务系统0到1落地实现方案
- Java描述设计模式(04):抽象工厂模式
- 《数学与泛型编程:高效编程的奥秘》一1.4 各章概述