LeetCode Permutations I && II

Permutations 

Given a collection of numbers, return all possible permutations.

For example,
[1,2,3] have the following permutations:
[1,2,3][1,3,2][2,1,3][2,3,1][3,1,2], and [3,2,1].

Permutations II 

Given a collection of numbers that might contain duplicates, return all possible unique permutations.

For example,
[1,1,2] have the following unique permutations:
[1,1,2][1,2,1], and [2,1,1].

不用修改,就两个问题都适用的算法,调用STL就行的。运行速度还是很快的。

class Solution {
public:
    vector<vector<int> > permuteUnique(vector<int> &num) {
        vector<vector<int> > permu;
		sort(num.begin(), num.end());
		permu.push_back(num);
		while (next_permutation(num.begin(), num.end()))
		{
			permu.push_back(num);
		}
		return permu;
	}
};


但是感觉做这些算法题,直接调用STL是不是有点感觉是作弊的嫌疑,呵呵O(∩_∩)O~

下面看看不使用STL如何做。

Permutations:

回溯法:

代码出自这个blog: http://blog.csdn.net/tuantuanls/article/details/8717262

只有手动走几遍吧,就能理解了。

这个算法是最简单的了

	vector<vector<int> > ret;
	int N;
	void perm(vector<int> &num, int i){
		if( i == N){
			ret.push_back(num);
		}

		for(int j = i; j < N; j++){
			swap(num[i], num[j]);
			perm(num, i + 1);
			swap(num[j], num[i]);
		}
	}

	vector<vector<int> > permute3(vector<int> &num) {
		N = num.size();
		ret.clear();
		perm(num, 0);
		return ret;
	}


 下面我用先序遍历树的思想写的代码,画个树理解就好了:

如第一次遍历:访问abc节点,取出a放入结果中,从abd节点到访问bc节点,访问节点c,把b放入结果中,最后访问c,把c放入结果中,每一条路径就是一个解,这里总共有6条路径,刚好是六个解。遍历完,答案就出来了。

和前面的回溯思想有一点差别,但是最基本思想差不多是一致的,要把这些思想融合起来,做到融会贯通! 

//5===============
	vector<vector<int> > permute5(vector<int> &num) {
		vector<int> mediRes;
		vector<vector<int> > res;
		permu(num, mediRes, res);
		return res;
	}

	void permu(vector<int> &num, vector<int> &mediRes, vector<vector<int> > &ret)
	{
		int N = num.size();
		if (N == 1)
		{
			mediRes.push_back(num[0]);
			ret.push_back(mediRes);
			mediRes.pop_back();
			return;
		}
		
		for (int i = 0; i < N; i++)
		{
			mediRes.push_back(num[i]);
			vector<int> cur = num;
			cur.erase(cur.begin()+i);
			permu(cur, mediRes, ret);
			mediRes.pop_back();//注意:别忘记了这里需要弹出,不要等到循环结束
		}
	}


 

 Permutations II 

这个问题主要就增加防止重复的判断就可以了。

两种方法:

1 使用sort排序,然后排除相邻的重复数字。注意没排序的不可以这么判断

2 利用一个新的容器,装已经处理的数字,不重复处理数字,利用map和set等都可以。

方法一程序:

vector<vector<int> > permuteUnique2(vector<int> &num) {
		mediRes.clear();
		res.clear();
		sort(num.begin(), num.end());
		permuII2(num);
		return res;
	}

	void permuII2(vector<int> &num)
	{
		int m = num.size();
		if (m == 1)	
		{
			mediRes.push_back(num[0]);
			res.push_back(mediRes);
			mediRes.pop_back();
			return;
		}
		//如果已经排序了的就可以值处理相邻的重复了
		for (int i = 0; i < m; i++)
		{
			while (i < m-1 && num[i] == num[i+1]) i++;
			mediRes.push_back(num[i]);
			vector<int> cur = num;
			cur.erase(cur.begin()+i);
			permuII2(cur);
			mediRes.pop_back();//注意:别忘记了这里需要弹出,不要等到循环结束
		}
	}


 

方法二程序:
vector<vector<int> > res;
	vector<int> mediRes;
	vector<vector<int> > permuteUnique(vector<int> &num) {
		mediRes.clear();
		res.clear();
		permuII(num);
		return res;
	}

	void permuII(vector<int> &num)
	{
		int m = num.size();
		if (m == 1)	
		{
			mediRes.push_back(num[0]);
			res.push_back(mediRes);
			mediRes.pop_back();
			return;
		}
		//注意:不能光判断相邻的是否重复,不相邻的重复也会出现重复结果的。
		//脑子要放大
		unordered_set<int> used;
		for (int i = 0; i < m; i++)
		{
			if (used.find(num[i]) == used.end())
			{
				mediRes.push_back(num[i]);
				vector<int> cur = num;
				cur.erase(cur.begin()+i);
				permuII(cur);
				mediRes.pop_back();//注意:别忘记了这里需要弹出,不要等到循环结束
				used.insert(num[i]);
			}
		}
	}


 2013.12.12Update更新一个特别的方法:

参考博客:

http://blog.csdn.net/tuantuanls/article/details/8717262
思路四
我觉得思路4是一个很常规的思路,很多把recursive的code改成iterative的code都会用到这样的方法,其实呢,它的本质就是把N个for改成while的方法。介个方法在编程之美里面的“电话号码”那一节提到过,不明白的童鞋可以去看一看,我觉得第一次想写粗来还是很难的,不过多写几个,就会很熟练啦


对应介个题目的思路捏就是。。。举个例子来说吧
如果我想求1,2,3,4的全排列
偶的思路就是建一个特殊的数,它的进位方法是 3, 2, 1, 0
所以,这个数的++过程就是
0000 -> 0010 -> 0100 -> 0110 ->0200 -> 0210 -> 
1000 -> 1010 -> 1100 -> 1110 ->1200 -> 1210 -> 
2000 -> 2010 -> 2100 -> 2110 ->2200 -> 2210 -> 
3000 -> 3010 -> 3100 -> 3110 ->3200 -> 3210
哇哈哈哈,刚好是24个!
然后捏? b0 b1 b2 b3就代表在当前剩下的数字中选择第bi个
哇!好复杂。。。
比如0210
0: 在1234中选择第0个,就是1
2: 在234中选择滴2个,就是4
1: 在23中选择第1个,就是3
0: 在2中选择第0个,就是2
所以0210对应点就素 1432

class Solution {
public:
	int factorial(int n)
	{
		int res = 1;
	
		for (int i = 2; i <= n; i++)
		{
			res *= i;
		}

		return res;
	}

	void addOne(vector<int> &table, const vector<int> &bound)
	{
		for (int i = table.size()-2; i >= 0; i--)
		{
			//可以是==的所以是>不是>=
			if (++table[i] > bound[i]) table[i] = 0;
			else break;
		}
	}

	vector<vector<int> > permute(vector<int> &num) 
	{ 
		int n = num.size();
		vector<vector<int> > res;
		if (n < 1) return res;

		vector<int> medRes(n);
		vector<int> temp(n);
		vector<int> table(n);

		vector<int> bound(n);
		for (int i = 0; i < n; i++)
		{
			bound[i] = n-1-i;
		}

		int fac = factorial(n);

		for (int i = 0; i < fac; i++)
		{
			temp = num;
			for (int j = 0; j < n; j++)
			{
				medRes[j] = temp[table[j]];
				temp.erase(temp.begin()+table[j]);
			}
			res.push_back(medRes);
			addOne(table, bound);
		}
		return res;
	}
};


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值