数字全排列组合c++实现(非递归)

21 篇文章 0 订阅
3 篇文章 0 订阅

题目:给出一个组无重复数字,比如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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值