前言:
今天,我给大家讲解的是关于全排列算。我会从三个方面去进行展开:
目录
前情摘要
(一)定义和公式讲解
1、定义
2、公式
(二)全排列的初始思想
(三)代码实现
1、递归不去重
2、递归去重
3、非递归实现
(四)题目讲解
1、字符串的排列
(五)总结
我们通过简单的举例带大家直观的认识一下:
假设现在数组中有【1 2 3】这样的三个元素,那么对其进行排列之后结果是什么呢?
我相信聪明的小伙伴们已经知道答案了,具体的如下:
假设现在数组中有【1 2 3 4】这样的四个元素,那么对其进行排列之后结果是什么呢?
具体的如下:
因此,综上所述,我们可以发现全排列得到的结果的数量跟数学中的全排列问题是一样的
1、上述我给出了一个序列的全排列的结果以及全排列得到的最终的结果数量。
2、接下来,我简单的讲述一下上述全排列是如何得到的,对其具体的实现过程进行描述。
具体过程如下:
2134
2143
2314
2341
2431
2413
通过上述的分析,我们不难发现一个问题那就是:
接下来,我们简单的实现一下这样的代码:
//生成向量的所有排列
void permute(string str, int left, int right)
{
//如果遍历到 left == right ,说明全排列已经做完了只需输出即可
if (left == right)
cout<
上面的程序乍一看没有任何问题了。但是当我们去想去打印输出时,看看运行的结果会怎么样:
那么此时很多小伙伴就要问了,我们想实现去重的排列可不可以呢?答案当然是可以的。具体如下:
去重跟不去重相差的其实就是对于元素的值的比较,我们设置一个 flag 标志位,来判断当前元素之后的元素是否于当前元素相同,再利用标志位去对其设限即可实现递归去重操作。
void generatePermute(vector& arry, int start, vector>& tmp)
{
if (start == arry.size()) {
tmp.push_back(arry);
return;
}
for (int i = start; i < arry.size(); i++) {
// 检查与前一个元素的交换是否会产生重复项
bool flag = false;
for (int j = start; j < i; j++) {
if (arry[i] == arry[j]) {
flag = true;
break;
}
}
if (!flag) {
swap(arry[start], arry[i]);
generatePermute(arry, start+1, tmp);
swap(arry[start], arry[i]);
}
}
}
vector> permute(vector& arry)
{
vector> tmp;
sort(arry.begin(), arry.end());
generatePermute(arry, 0, tmp);
return tmp;
}
int main()
{
vector arry = {1, 1, 2};
vector> res = permute(arry);
for (auto& e1 : res)
{
for (auto& e2 : e1)
{
cout << e2 << " ";
}
cout << endl;
}
return 0;
}
我们在把 arry数组中的元素换为【1 2 2】,看最终结果是否正确,具体如下:
permute 函数首先对输入向量进行排序,以将相同值的元素分组在一起。
由于非递归的方法是基于对元素大小关系进行比较而实现的,所以这里暂时不考虑存在相同数据的情况。
void permute(vector& arry)
{
sort(arry.begin(), arry.end());
stack> tmp;
tmp.push({-1, 0});
while (!tmp.empty())
{
int i = tmp.top().first, j = tmp.top().second;
tmp.pop();
if (i >= 0)
swap(arry[i], arry[j]);
if (j == arry.size() - 1) {
// 输出排列
for (int k = 0; k < arry.size(); k++)
{
cout << arry[k] << " ";
}
cout << endl;
}
else
{
for (int k = arry.size() - 1; k > j; k--)
{
tmp.push({j, k});
}
tmp.push({-1, j+1});
}
}
}
int main()
{
vector arry = {0,2,2};
permute(arry);
return 0;
}
接下来,我通过LeetCode上的三道题目给大家练练手,帮助大家理解记忆!!!
链接如下:字符串的排列
题目如下:
思路讲解:
代码展示:
class Solution {
public:
vector arry;
vector res;
void dfs(const string &s, int num, string &tmp)
{
//临时字符串满了加入输出
if(num == s.size())
{
res.push_back(tmp);
return;
}
//遍历所有元素选取一个加入
for(int i=0; i permutation(string s) {
//先对字符串进行排序处理
sort(s.begin(), s.end());
//标记每个位置的字符是否被使用过s
string tmp;
arry=vector(s.size());
dfs(s,0,tmp);
return res;
}
};
代码解释:
1️⃣ 首先优先按照字典顺序进行排序;
2️⃣ 创建一个 arry 数组标记字典中的字母是否被选择;
3️⃣紧接着就进入dfs 函数进行递归操作;
4️⃣写出递归的条件:
①如果填充的字符串已经到达该最后的长度,那么说明这个字符串就是我们需要的;
②紧接着进行遍历操作,查找还未使用的序列,并尝试使用该字母:
③如果我们已经使用了【i】位置处的值,那么后面与它相等的就不在使用,直接进行 ++操作
结果展示:
还有两道题,一道是去重的,另外一道是不去重的,大家可以自己尝试做做看,如果以下两题自己会做了,那么对于全排列算法,大家基本上就掌握了。
链接如下:全排列
链接如下:全排列 ||
到此,关于全排列算法就讲到这里了。希望本文对大家有所帮助,感谢各位的观看!!