排列组合与子集
来源:互联网 发布:6300hq和7300hq知乎 编辑:程序博客网 时间:2024/06/05 17:07
Leetcode上有几道题比较类似,Permutation、Combination和Subsets等。说它们相似,是因为思路类似。要求出某个序列的排列组合和子集,初看起来,要对序列中的每个元素排列、取舍,初看起来,似乎要写一个循环层数正比于元素个数的大循环,而语言并没有提供这种功能。这时就要转变思路,先求部分解或子问题的解。
例如Permutation这题:
vector<vector<int>> permute(vector<int>& nums) { vector<vector<int> > result; vector<int> temp; result.push_back(nums); /* 每次确定一个字符,第i个字符之前的序列确定后,第i个字符有nums.size()-i种选择。通过将第i个字符与第i+1到第nums.size()-1个字符交换得到不同的排列。 类似于数学归纳法?动态规划?迭代计算出所有排列 */ for(int i = 0; i < nums.size()-1; ++i) { /* 必须倒序遍历,从算法逻辑和实现正确性上考虑都必须如此。初始式j = result.size()-1只会执行一次,若采用顺序遍历, j < result.size()每次都会执行,使result.size()重新计算,而循环每执行一次,result.size()都会增大,导致无穷循环 */ for(int j = result.size()-1; j >= 0; --j) { for(int k = i+1; k < nums.size(); ++k) { temp = result[j]; swap(temp[i], temp[k]); result.push_back(temp); } } } return result; }大体思路就是每次只考虑(一个)子问题,构造出部分解或子问题的解,然后再通过操作部分解或子问题的解来构造更完整的解,迭代直到构造出所有解。也就是说每个解的构造过程不是独立的,较大问题的解需要依赖较小问题的解。
Subsets这题的代码可能能更好地展示这种方法:
/* 求所有子集,就是从原集合中取出若干元素组成新的集合,每个元素要么取要么不取,共有2^n种情况。本题和求排列的问题十分相似,因此思路几乎一致。求n个元素的集合其子集,那就先求前n-1个元素之集合的子集。对于前n-1个元素来说,根本就不知道第n个元素的存在,因此无须考虑,相当于先求子问题的解,类似于动态规划?对于原问题的集合来说,相当于第n个元素不取,先求前n-1个元素的子集,待求得后,再将第n个元素添加进去,即得原问题的解。 思路总结如下:每个元素要么取要么不取,先求第0~i元素的子集,第i+1~n-1个元素不考虑,相当于不取;待求得前i个元素的子集后,再将第i+1个元素添加进去,即得前i+1个元素的子集;而当求前i个元素的子集时,前i-1个元素的子集已经构造完毕,我们只需遍历这些集合,将第i个元素添加进去就行。 与求排列问题不同的是,求排列是先得出部分解,再通过部分解迭代构造出所有解;求子集,是先求子问题的解(虽然子问题的解也是原问题的部分解),再对子问题的解扩展得到更大子问题的解,迭代直到得出原问题的所有解。*/ vector<vector<int>> subsets(vector<int>& nums) { sort(nums.begin(), nums.end()); vector<vector<int> > result; vector<int> temp; result.push_back(temp); for(int i = 0; i < nums.size(); ++i) { int n = result.size(); for(int j = 0; j < n; ++j) { temp = result[j]; temp.push_back(nums[i]); result.push_back(temp); } } return result; }
求子集时,我们不是独立地构造每个解,而是在求得的子问题的解中再添加元素得到新的(更大子问题)解。例如,空集{}是集合{1,2,3}的子集,那么在空集中添加元素1,得到集合{1}是原集合的子集,再向集合{1}中添加元素2得到{1,2}就是原问题的更大的子集。
问题本身都不难,都是常见的问题,但是其中的思路具有通用性和普适性。有点难度的东西不会也就算了,(其实我也学过一些算法,动态规划什么的,但始终不得要领,学的时候似乎没什么问题,用的时候就不会了,再回过头看,当时可能回了,以后遇到又不会了,反反复复如此,说到底,还是手生,练得少。这个其实不光是学算法、编程如此,学一切知识都可能出现这种情况,是一个普遍性问题),简单的东西就要保证会且熟。根据我的经验,简单的知识就可以解决大部分问题了,很多问题并不需要多高深的知识,人们常常在实践中用到了一些复杂的技术却反而忘记了基本的原则,这就本末倒置了。并且,如果熟练掌握了简单的知识,在需要更高深知识的情况下,也可以较容易地进一步学习,这就是打好基础的作用。
在以后的学习和工作中,希望自己一方面思维能够更开阔、活跃,另一方面,也要更踏实一些。
0 0
- 排列组合与子集
- 【算法总结-排列组合与子集问题】排列组合与子集问题
- 【算法总结-排列组合与子集问题】排列组合与子集问题
- 排列组合,子集生成问题,与nyoj 组合数
- [模板]排列组合子集专题
- 排列组合子集合的算法
- 回溯法求子集,排列组合
- 位运算与子集
- 排列组合与回溯算法
- 排列组合与回溯算法
- 动态规划与排列组合
- 动态规划与排列组合
- 排列组合与回溯算法
- 动态规划与排列组合
- 动态规划与排列组合
- 动态规划与排列组合
- 排列组合与回溯算法
- 母函数与排列组合
- Could not read from remote repository.
- Objective-C Runtime 运行时之三:方法与消息
- VC工程下的各种后缀文件
- iOS开发- 文件共享(利用iTunes导入文件, 并且显示已有文件)
- 《研磨struts2》第二十一章 零配置 之 21.2 约定大于配置
- 排列组合与子集
- strconv — 字符串和基本数据类型之间转换
- 面试常见的问题(多线程同步 socket通信等)
- 使用git管理github项目
- 一起来当网管(一)——Windows Server上的DHCP配置
- Deep Learning Open Sources
- iOS利用Runtime自定义控制器POP手势动画
- 简单的android ios抓包技巧
- 德州扑克AI简介——2015华为软件精英赛小结