问题概述
题目:算法-给定序列的全排列
问题描述:
全排列(full permutation)
给定一个没有重复数字的序列,返回其所有可能的全排列。
输入:
给定一个多元素的整形数组;
输出:
这个整形数组所有可能的排列;
样例输入:
1 2 3
样例输出:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
1. 问题分析
1.1 算法核心
本题是关于全排列的算法的一个基础应用;
全排列算法有很多的解法,而本文分享的是标志数组回溯的解法,算法核心就是回溯的核心思想-向内推导,也就是由底向顶推导的算法;
1.2 算法分析
本题与其他的全排列算法,题目的输入是一个一维整形数组,其中包含了我们用来排列的数字,输出是要我们输出所有的排列组合,也就是说我们需要动态的找到每个排列,然后存储起来,最后全部一起输出,也可以每找到一个排列就输出一次,这个看大家的代码设计,都是可以的,我这里采用的是二维vector动态数组存储所有的排列组合,最后一并输出;
首先我们获取输入就需要一个vector存储数字,然后每次从缓冲区中读取一个数字压到vector中,读入完成后,直接调用全排列函数开始找所有的排列;
全排列的直观想法就是枚举所有可能的排列,但是那样的话这个算法时间复杂度就太大了!(On2)所以我们要对特殊情况做特殊处理,这样才能减少时间复杂度,最终达到本博客分享的算法的时间复杂度O(n!);
首先我们解释一下generatePermutation这个函数的参数意义,这样就能快速理解算法思想了,第一个参数res是二维动态数组,采用引用的方式这样就可以直接调用结果数组存储每一次的排列结果了,第二个参数nums是我们输入的数字数组,这样我们每次调用函数的时候才知道哪几个数字可以调用,第三个参数是temp,目的是提供一个临时一维动态数组存储每次的排列结果,最后temp元素数达到nums元素数表明已经找到了一个新的排列我们就可以停止递归了,第四个参数是used数组,我们的标志数组,第五个参数是使用数字的个数,用来检查递归条件的;
说到这里,是不是已经有大致的思路了,好,接下里我们直接看函数实现部分,permutation函数是一个总的调用的函数,用来开始我们的全排列,先定义一个临时二维动态数组res,判断若是读入的数组个数为0说明根本不用排列,直接返回即可;
然后定义一个临时标志数组,与nums元素对应即可,就是有几个要排列的数字就需要几个标志嘛,全都初始化为false;
接下来再定义一个临时数组tmp,直接进行排列递归函数即可,反正我们的参数是引用,最后返回临时二维动态数组res即可;
接下来看核心排列递归函数,第一部分先判断是否已经达到递归边界,即nums的元素数是否已经等于tmp的元素数,若等于说明已经排列好一个序列,res中压入这个tmp,然后直接返回结束递归;
若没有到递归边界,就从输入数组中取数字,取到第一个没取用过的数字的时候,先将他的标志设置为true,接着压入temp,这样序列就建立起来了,接着继续递归,直到达到递归边界,最后返回的时候,要记得
把标志重新设置为false,然后弹出这个元素,这样下一次找排列的时候才可以继续找排列,相当于是我们已经找到了一个序列,然后返回时候进行清理工作!
2. 解决方案
#include <iostream>
#include <vector>
using namespace std;
vector<vector<int>> permutation(vector<int> nums);
void showResult(vector<vector<int>> res);
void generatePermutation(vector<vector<int>> &res, vector<int> &nums, vector<int> &temp, vector<bool> &used, int usedNums);
int main(void)
{
vector<vector<int>> result;
vector<int> inputNumVec;
int inputNumber;
while ((cin >> inputNumber) && inputNumber != EOF)
inputNumVec.push_back(inputNumber);
result = permutation(inputNumVec);
showResult(result);
return 0;
}
void showResult(vector<vector<int>> res)
{
for (int i = 0; i < res.size(); i++)
for (int k = 0; k < res[i].size(); k++)
{
if (k + 1 != res[i].size())
cout << res[i][k] << " ";
else
cout << res[i][k] << endl;
}
}
vector<vector<int>> permutation(vector<int> nums)
{
vector<vector<int>> res;
if (nums.size() == 0)
return res;
vector<bool> usedNumSign(nums.size(), false);
vector<int> tmp;
generatePermutation(res, nums, tmp, usedNumSign, 0);
return res;
}
void generatePermutation(vector<vector<int>> &res, vector<int> &nums, vector<int> &temp, vector<bool> &used, int usedNums)
{
if (usedNums == nums.size())
{
res.push_back(temp);
return;
}
for (int i = 0; i < nums.size(); i++)
{
if (!used[i])
{
used[i] = true;
temp.push_back(nums[i]);
generatePermutation(res, nums, temp, used, usedNums + 1);
used[i] = false;
temp.pop_back();
}
}
}
3. 资源分享
旗木白哉のGitHub:
https://github.com/YangMengHeng
GitHub/Gitee源代码下载地址:
GitHub
https://github.com/YangMengHeng/myCode/tree/master/VSCode
https://github.com/YangMengHeng/myCode/tree/master/Java
Gitee
https://gitee.com/QMBZ/myCode/tree/master/VSCode
https://gitee.com/QMBZ/myCode/tree/master/Java
旗木白哉のblog源代码下载地址:
https://github.com/YangMengHeng/myCode/tree/master/%E6%97%97%E6%9C%A8%E7%99%BD%E5%93%89%E3%81%AEblog
https://gitee.com/QMBZ/myCode/tree/master/%E6%97%97%E6%9C%A8%E7%99%BD%E5%93%89%E3%81%AEblog