题目:给出一个组无重复数字,比如1, 2, 3, 4 ;求这4个数字在不重复使用的情况下,可以组成哪些数字?(求全排列)
思路:
1)将排序作为多叉树深度遍历来实现,
2)每个数字作为根节点,都可以深度遍历一次;从根每次深度增加,即是增加了前缀,从此前缀为根又可以深度遍历;
3)非递归的关键在于使用栈,当栈不为空,则说明遍历没有结束;
4)在当前前缀下,比如12,如果发现还有没有使用的数字,比如4,则应该入栈124,并以124为栈顶作为前缀继续遍历;
5)当发现当前栈顶以及使用了所有的数字,则说明完成当前前缀的探索,则应该退栈;
直接上代码,
// testNum.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <vector>
#include <stack>
#include <map>
using namespace std;
using UsedArray = std::vector<bool>;
using DataArray = std::vector<int>;
// 检查标记数组,是否所有的都被使用了
int isUsedAll(const UsedArray& used)
{
for (size_t i=0; i<used.size(); i++)
{
if (used[i] == false)
return (int)i;
}
return -1;
}
// 根据前缀,标记已经使用的部分
void markUsed(const DataArray& dataNew, const DataArray& numbers, UsedArray &usedNew)
{
// 倒索引
static std::map<int, int> numMap;
if (numMap.size() == 0)
{
for (int i = 0; i < numbers.size(); i++)
{
numMap[numbers[i]] = i;
}
}
for (size_t i = 0; i < dataNew.size(); i++) // 根据当前前缀,重新确定标记
{
int data = dataNew[i];
int index = numMap[data];
usedNew[index] = true;
}
}
// 当数字栈为空时返回,
// 当前标记全使用完了,则输出序列,并退栈
// 当前标记没有使用完,则先选下一个可用的,标记,并对2个栈同时入栈
// 每一轮前缀的深度过程中,前缀部分不变,但是使用标记不停地增加,直到所有的标记都用完
void PermutationOneRoot(const DataArray &numbers,
std::stack<DataArray>& stackNum,
std::stack<UsedArray> &stackUsed,
std::vector<DataArray>& result)
{
while (!stackNum.empty())
{
UsedArray& currentUsed = stackUsed.top();
int index = isUsedAll(currentUsed); // 找到下一个未使用的索引
if (-1 == index)
{
if (stackNum.top().size() == numbers.size())
result.push_back(stackNum.top());
stackNum.pop();
stackUsed.pop();
}
else
{
DataArray dataNew = stackNum.top(); // 复制当前前缀,并添加一个数
dataNew.push_back(numbers[index]);
stackNum.push(dataNew);
currentUsed[index] = true; // 重要!当前栈顶表示当前 前缀 下,用了多少
UsedArray usedNew = UsedArray(numbers.size(), false); // 生成一个新的栈顶标记数组,
markUsed(dataNew, numbers, usedNew);
stackUsed.push(usedNew); // 入栈
}
}
}
// 输入一组数字,输出结果
void Permutation(const DataArray& numbers, std::vector<DataArray>& result)
{
std::stack<DataArray> stackNum;
std::stack<UsedArray> stackUsed;
// 每个数字分别作为前缀执行一遍:
for (int i=0; i<numbers.size(); i++)
{
// 当前数字为根节点的前缀
stackNum.push({ numbers[i] });
UsedArray used(numbers.size(), false);
used[i] = true;
stackUsed.push(used);
// 以每个数字为根节点,分别执行一次深度遍历排列
PermutationOneRoot(numbers, stackNum, stackUsed, result);
//cout << "---------------" << endl;
}
}
void printData(std::vector<DataArray>& result)
{
for (size_t i = 0; i < result.size(); i++)
{
DataArray & data = result[i];
for (int i : data)
{
cout << i << " ";
}
cout << endl;
}
}
int main()
{
DataArray nums{1, 2, 3, 4};
std::vector<DataArray> result;
Permutation(nums, result);
printData(result);
return 0;
}
输出结果:
1 2 3 4
1 2 4 3
1 3 2 4
1 3 4 2
1 4 2 3
1 4 3 2
2 1 3 4
2 1 4 3
2 3 1 4
2 3 4 1
2 4 1 3
2 4 3 1
3 1 2 4
3 1 4 2
3 2 1 4
3 2 4 1
3 4 1 2
3 4 2 1
4 1 2 3
4 1 3 2
4 2 1 3
4 2 3 1
4 3 1 2
4 3 2 1