全排列问题

全排列系列问题(持续更新... ...)

46. 全排列

给定一个 没有重复 数字的序列,返回其所有可能的全排列。

示例:

输入: [1,2,3]
输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutations

经典的一道回溯算法题,我们需要找出所有可能的排列组合,共有 n!种排列的方式。
图解↓
在这里插入图片描述
回溯算法代码:

class Solution {
public:
	vector<vector<int>> result;//结果集
	vector<int> path;//访问过的元素:标记数组
	void backTrack(vector<int>& nums,vector<bool>& used){
		if (path.size() == nums.size()) {
			result.push_back(path);//加入结果集
			return;
		}

		for (int i = 0; i < nums.size(); i++) {
			if (used[i]) continue;//元素被使用了,跳过

			used[i] = 1;//未使用,标记为1
			path.push_back(nums[i]);//当前元素放入path中
			backTrack(nums, used);
			path.pop_back();
			used[i] = 0;
		}
	}

	vector<vector<int>> permute(vector<int>& nums) {
		result.clear();
		path.clear();
		vector<bool> used(nums.size(), false);
		backTrack(nums, used);
		return result;
	}
};

47. 全排列 II

给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。

示例 1:
输入:nums = [1,1,2]
输出: [[1,1,2], [1,2,1], [2,1,1]]

示例 2:
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutations-ii

思路:
与上题不同的是,数组中含有重复元素,这时候为避免重复我们需要对其进行去重操作,首先我们需要对数组进行排序,保证重复元素可以被我们的剪枝条件滤出(排序后相同元素一定是相连的),具体实现见如代码:

在这里插入图片描述

class Solution {
public:
	pair<int, int> HolandNationalFlag(vector<int>& nums, int start ,int end) {//荷兰国旗法快速排序
		int left = start - 1, right = end + 1;
		int flag = nums[start];
		int index = start;
		while (index < right) {
			if (nums[index] == flag)
				index++;
			else if (nums[index] < flag)
				swap(nums[index++], nums[++left]);
			else
				swap(nums[index], nums[--right]);
		}
		return make_pair(left, right);
	}
	void QuickSort(vector<int>& nums, int start, int end) {
		if (start >= end) return;//只有一个元素,递归结束
		pair<int, int> p = HolandNationalFlag(nums, start, end);
		QuickSort(nums, start, p.first);//递归调用
		QuickSort(nums, p.second, end);
	}

	vector<vector<int>> result;
	vector<int> path;
	void backtrack(vector<int>& nums, vector<bool>& used) {
		if (path.size() == nums.size()) {
			result.push_back(path);
			return;
		}
		for (int i = 0; i < nums.size(); ++i) {
            //used[i - 1] == 1 说明同一树枝 nums[i - 1]使用过
            //used[i - 1] == 0 ,说明同一层nums[i - 1]使用过
			if (i > 0 && nums[i - 1] == nums[i] && !used[i - 1]) {
				//去重,同一层中若相同元素已经使用过就跳过当前元素
				continue;
			}
			if (!used[i]) {
				used[i] = true;
				path.push_back(nums[i]);
				backtrack(nums, used);
				path.pop_back();
				used[i] = false;
			}
		}
	}

	vector<vector<int>> permuteUnique(vector<int>& nums) {
		result.clear();
		path.clear();
		vector<bool> used(nums.size(), false);
        sort(nums.begin(),nums.end());
		backtrack(nums, used);
		return result;
	}
};

如上使用了数组对每个使用过的元素进行记录,通过判断:

if (i > 0 && nums[i - 1] == nums[i] && !used[i - 1])
				//去重,同一层中若相同元素已经使用过就跳过当前元素

消除重复元素。
图中清晰地显示了判断语句的作用: 防止同一拓展节点选择了相同的子节点,对于每个节点来说,其孩子节点不可以选择相同元素,因此另外一种思路: 每层选择孩子节点时使用哈希集合unordered_set记录,如果在选择过程中遇到了相同的元素(之前已经选择过),那么本次选择无效。
上代码:

class Solution {
public:
	vector<vector<int>> res;
	void dfs(vector<int>& nums, int& n, int depth) {
		if (depth == n - 1) {//此时已经完成了[0:n - 2]元素的选择,因此最后一个元素nums[n - 1]已经确定, 退出递归
			res.push_back(nums);
			return;
		}
		unordered_set<int> join;
		for (int i = depth; i < n; ++i) {
			if (join.count(nums[i]) == 1)//在之前已经选择过相同的元素
				continue;
			join.insert(nums[i]);
			swap(nums[i], nums[depth]);//选择 nums[i]
			dfs(nums, n, depth + 1);
			swap(nums[i], nums[depth]);//再次交换,撤销之前的选择
		}
	}

	vector<vector<int>> permuteUnique(vector<int>& nums) {
		int n = nums.size();
		dfs(nums, n, 0);
		return res;
	}
};

三连击

题目描述:
将 1, 2, … , 91,2,…,9 共 9 个数分成 3 组,分别组成 3 个三位数,且使这 3 个三位数构成 1 : 2 : 3 的比例,试求出所有满足条件的 3个三位数。
查看洛谷官网原题描述

思路

使用回溯算法对1~9共九个数字进行全排列,将结果保存于 result 数组中,最后对 result 遍历求出符合条件的值。
直接对所有数字进行回溯以及result遍历,算法调用内存栈层数过高,进程内存达到123MB

未剪枝进行回溯内存情况
其后,我对其进行剪枝操作,剪枝的位置位于对result数组遍历时去除了明显不满足答案的值,峰值依旧到达了123MB,最后将剪枝放到回溯过程中,进程内存减少到了22MB!!!
进行剪枝后的内存占用情况
代码:(未剪枝,剪枝部分代码已打注释)

#include <iostream>
#include <vector>
using namespace std;

vector<vector<int>> result;
vector<int> path;

void backTrack(vector<int>& nums, vector<bool>& used,int depth) {
	if (path.size() == nums.size()) {
		result.push_back(path);
		return;
	}
	for (int i = 0; i < nums.size(); i++) {
		if (used[i]) {
			continue;
		}
		//回溯时进行剪枝↓
		/*if (depth == 1 && nums[i] > 3 || depth == 4 && nums[i] > 6 || depth == 7 && nums[i] < 3)
		{
			continue;
		}*/
		used[i] = true;	
		path.push_back(nums[i]);	
		backTrack(nums, used,depth + 1);
		path.pop_back();
		used[i] = false;
	}
}

vector<int> ans;
void threeAttack() {
	int nums1,nums2,nums3;
	for (int i = 0; i < result.size(); i++) {
		//遍历数组时进行剪枝↓
		//剪枝,显然这些情况可以直接排除
		/*if (result[i][0] > 3) continue;
		else if (result[i][3] > 6) continue;
		else if (result[i][6] < 3) continue;*/
		nums1 = result[i][0] * 100 + result[i][1] * 10 + result[i][2];
		nums2 = result[i][3] * 100 + result[i][4] * 10 + result[i][5];
		nums3 = result[i][6] * 100 + result[i][7] * 10 + result[i][8];
		if (nums2 == 2 * nums1 && nums3 == 3 * nums1) {
			cout << nums1 << "  " << nums2 << "  " << nums3 << endl;
		}
	}
}

vector<vector<int>> total_queue(vector<int>& nums) {
	vector<bool> used(nums.size(), false);
	backTrack(nums, used,1);
	threeAttack();
	return result;
}

int main() {
	/*https://blog.csdn.net/Genius_bin*/
	/*author : @nepu_bin*/
	vector<int> nums{ 1,2,3,4,5,6,7,8,9 };
	total_queue(nums);
	return 0;
}

运行效果:
运行图

  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nepu_bin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值