题意分析
- 给你一个序列,需要找到这个序列的所有的子序列的集合,要求这些子序列的内部需要按照元素的大小进行升序排列。
- 前置知识
- 什么是子序列?子序列是一个序列里面选择若干个元素,这些元素要保持初始的相对位置,然后组成的一个新的序列就叫做这个初始序列的子序列。
思路分析
解法一 回溯法
对于每个元素,我们知道,有选择和不选的区别,这其实就是一个很明显的递归的操作。所以我们可以使用回溯法进行求解。我们按照顺序遍历这个序列,这是为了保持数据的升序。然后,当我们收集到某一个元素的时候,我们需要在进行完这一次DFS的时候进行回溯,就是不选择这个元素继续进行递归处理。
代码如下
- 递归操作,需要选择到每个位置,时间复杂度为O(2^n)
- 利用了一个unordered_map进行哈希处理,同时使用了一个全局vector,空间复杂度为O(n)
class Solution { public: vector<vector<int> > ans; unordered_map<vector<int>,bool> mp; vector<int> v; int n; void dfs(int cnt,vector<int> &S){ // 如果这个数组之前没有出现过,那么记录下来 if(mp[v]==false){ ans.push_back(v); mp[v]=true; } // 递归出口 if(cnt==n){ return; } dfs(cnt+1,S); v.push_back(S[cnt]); dfs(cnt+1,S); // 回溯 v.pop_back(); } vector<vector<int> > subsets(vector<int> &S) { n=S.size(); // 由于子集需要升序排列,所以先进行排序 sort(S.begin(),S.end()); dfs(0,S); return ans; } };
解法二 迭代
- 这个思想是比较巧妙地一个思想,由于最初始的序列保持了元素的升序,那么我们可以按照顺序进行迭代,当然,我们首先需要将一个空的序列放入最终的答案,然后我们每次添加一个元素,就相当于在目前已知的所有的序列里面新添加一个元素,这就是本题迭代的思想。
- 具体请看下图的过程。
- 代码如下
- 我们对于每个元素的迭代的此时为O(1+2+3+4+..+n)=O(),但是,我们看在第二重循环里面还有一个vector的直接赋值,那么这里也是,即时间复杂度为
- 程序中途使用了一个tmp进行处理,空间复杂度为O(n)
class Solution { public: vector<vector<int> > subsets(vector<int> &S) { vector<vector<int> > ans; ans.push_back({}); // 大循环枚举为了保证元素是按照升序排列 for(auto x:S){ int n=ans.size(); // 小循环枚举是为了在这个x添加进来之后更新之前全部的序列 for(auto i=0;i<n;i++){ vector<int> tmp(ans[i]); tmp.push_back(x); ans.push_back(tmp); } } return ans; } };