Subsets I

Given a set of distinct integers, nums, return all possible subsets.

Note:

  • Elements in a subset must be in non-descending order.
  • The solution set must not contain duplicate subsets.

For example,
If nums = [1,2,3], a solution is:

[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]

思路一(http://www.cnblogs.com/felixfang/p/3775712.html)

可以用递推的思想,观察S=[], S =[1], S = [1, 2] 时解的变化。

可以发现S=[1, 2] 的解就是 把S = [1]的所有解末尾添上2,然后再并上S = [1]里面的原有解。因此可以定义vector<vector<int> > 作为返回结果res, 开始时res里什么都没有,第一步放入一个空的vecotr<int>,然后这样迭代n次,每次更新res 内容,最后返回res。

代码:

class Solution {
public:
vector<vector<int> > subsets(vector<int> &S) {
vector<vector<int> > res;
vector<int> emp;
res.push_back(emp);
sort(S.begin(), S.end());
if(S.size() == ) return res;
for(vector<int>::iterator ind = S.begin(); ind < S.end(); ++ind){
int size = res.size();
for(int i = ; i < size; ++i){
vector<int> v;
for(vector<int>::iterator j = res[i].begin(); j < res[i].end(); ++j){
v.push_back(*j);
}
v.push_back(*ind);
res.push_back(v);
}
}
return res;
}
};

思路二

所谓子集,就是包含原集合中的一些元素,不包含另一些元素。如果单独看某一个元素,它都有两种选择:"被包含在子集中"和"不被包含在子集中",对于元素个数为n、且不含重复元素的S,子集总数是2n。因此我们可以遍历S的所有元素,然后用递归考虑每一个元素包含和不包含的两种情况。

代码,这种思路需要用到递归

(代码来自http://www.acmerblog.com/leetcode-solution-subsets-6227.html 这篇博客可以好好看看

// LeetCode, Subsets

// 增量构造法,深搜,时间复杂度O(2^n),空间复杂度O(n)

class Solution {
public:
vector<vector<int> > subsets(vector<int> &S) {
sort(S.begin(), S.end()); // 输出要求有序
vector<vector<int> > result;
vector<int> path;
subsets(S, path, , result);
return result;
} private:
static void subsets(const vector<int> &S, vector<int> &path, int step,
vector<vector<int> > &result) {
if (step == S.size()) {
result.push_back(path);
return;
}
// 不选S[step]
subsets(S, path, step + , result);
// 选S[step]
path.push_back(S[step]);
subsets(S, path, step + , result);
path.pop_back();
}
};

思路同上,代码中可以采用位向量法,开一个位向量bool selected[n],每个元素可以选或者不选。

// LeetCode, Subsets
// 位向量法,深搜,时间复杂度O(2^n),空间复杂度O(n)
class Solution {
public:
vector<vector<int> > subsets(vector<int> &S) {
sort(S.begin(), S.end()); // 输出要求有序
vector<vector<int> > result;
vector<bool> selected(S.size(), false);
subsets(S, selected, , result);
return result;
}
private:
static void subsets(const vector<int> &S, vector<bool> &selected, int step,
vector<vector<int> > &result) {
if (step == S.size()) {
vector<int> subset;
for (int i = ; i < S.size(); i++) {
if (selected[i]) subset.push_back(S[i]);
}
result.push_back(subset);
return;
}
// 不选S[step]
selected[step] = false;
subsets(S, selected, step + , result);
// 选S[step]
selected[step] = true;
subsets(S, selected, step + , result);
}
};

Subsets II

Given a collection of integers that might contain duplicates, nums, return all possible subsets.

Note:

  • Elements in a subset must be in non-descending order.
  • The solution set must not contain duplicate subsets.

For example,
If nums = [1,2,2], a solution is:

[

[2],

[1],

[1,2,2],

[2,2],

[1,2],

[]

]

思路一:

其实比较简单,就是每当遇到重复元素的时候我们就只把当前结果集的后半部分加上当前元素加入到结果集中,因为后半部分就是上一步中加入这个元素的所有子集,上一步这个元素已经加入过了,前半部分如果再加就会出现重复。所以算法上复杂度上没有提高,反而少了一些操作,就是遇到重复时少做一半,不过这里要对元素集合先排序,否则不好判断重复元素。同样的还是可以用递归和非递归来解,不过对于重复元素的处理是一样的。

我们以S=[1,2,2]为例:

可以发现从S=[1,2]变化到S=[1,2,2]时,多出来的有两个子集[2,2]和[1,2,2],这两个子集,其实就是 [2], [1,2]末尾都加上2 而产生。而[2], [1,2] 这两个子集实际上是 S=[1,2]的解到 S=[1]的解 新添加的部分。

因此,若S中有重复元素,可以先排序;遍历过程中如果发现当前元素S[i] 和 S[i-1] 相同,那么不同于原有思路中“将当前res中所有自己拷贝一份再在末尾添加S[i]”的做法,我们只将res中上一次添加进来的子集拷贝一份,末尾添加S[i]。


class Solution {
public:
vector<vector<int> > subsetsWithDup(vector<int> &S) {
vector<vector<int> > subsets;
vector<int> v;
subsets.push_back(v);
if(S.empty()) return subsets;
sort(S.begin(), S.end());
int m = 0; //m 用来存储上一次加进来的子集们的起始index
for(vector<int>::iterator i = S.begin(); i < S.end(); ++i){
int start = ((i != S.begin() && *i == *(i-1)) ? m : 0); //如果S的当前元素和前一个元素相同,只拷贝上次加进来的子集
int end = subsets.size();
for(int j = start; j < end; ++j){
vector<int> vt;
for(vector<int>::iterator k = subsets[j].begin(); k < subsets[j].end(); ++k){
vt.push_back(*k);
}
vt.push_back(*i);
subsets.push_back(vt);
}
m = end;
}
return subsets;
}
};

思路二:

对于含有重复元素的S,可以先排序,然后考虑去重:我们可以发现如果所遍历的当前元素S[i] 和 目前的子集的末尾元素相同,那么就不再需要考虑"不包含当前元素到子集中"的情况,只需要考虑"包含当前元素到子集中一种情况"。举个例子:对于S=[1,2,2],如果遍历到第二个"2",当前子集v是[1, 2],这个时候如果考虑"不把2包含进子集的情况",即维持子集=[1,2]不动,遍历下一个元素;这样其结果会出现重复。因为考虑另一个递归调用,其当前子集v是[1],也遍历到了S的第二个"2",它将这个"2"元素放入当前子集,虽然继续遍历下一个元素。这两个递归调用的结果是重复的。因此,若当前递归调用所遍历到的元素和当前子集v的末尾元素相同,只考虑"把当前元素添加到子集末尾"的情况。

// LeetCode, Subsets II
// 增量构造法,版本1,时间复杂度O(2^n),空间复杂度O(n)
class Solution {
public:
vector<vector<int> > subsetsWithDup(vector<int> &S) {
sort(S.begin(), S.end()); // 必须排序
vector<vector<int> > result;
vector<int> path;
dfs(S, S.begin(), path, result);
return result;
}
private:
static void dfs(const vector<int> &S, vector<int>::iterator start,
vector<int> &path, vector<vector<int> > &result) {
result.push_back(path);
for (auto i = start; i < S.end(); i++) {
if (i != start && *i == *(i-)) continue;
path.push_back(*i);
dfs(S, i + , path, result);
path.pop_back();
}
}
};

Subsets I&&II——经典题的更多相关文章

  1. zoj 1081:Points Within(计算几何,判断点是否在多边形内,经典题)

    Points Within Time Limit: 2 Seconds      Memory Limit: 65536 KB Statement of the Problem Several dra ...

  2. poj 1611:The Suspects(并查集,经典题)

    The Suspects Time Limit: 1000MS   Memory Limit: 20000K Total Submissions: 21472   Accepted: 10393 De ...

  3. Hihicoder 题目1 : Trie树(字典树,经典题)

    题目1 : Trie树 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编 ...

  4. poj 3264:Balanced Lineup(线段树,经典题)

    Balanced Lineup Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 32820   Accepted: 15447 ...

  5. poj 2503:Babelfish(字典树,经典题,字典翻译)

    Babelfish Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 30816   Accepted: 13283 Descr ...

  6. poj 2001:Shortest Prefixes(字典树,经典题,求最短唯一前缀)

    Shortest Prefixes Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 12731   Accepted: 544 ...

  7. hdu 1247:Hat’s Words(字典树,经典题)

    Hat’s Words Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total ...

  8. hdu 1075:What Are You Talking About(字典树,经典题,字典翻译)

    What Are You Talking About Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 102400/204800 K ...

  9. hdu 1251:统计难题(字典树,经典题)

    统计难题 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131070/65535 K (Java/Others)Total Submi ...

随机推荐

  1. wildcard ,notdir ,patsubst ,obj=$(dir:%.c=%.o)

    Makefile中wildcard的介绍 在Makefile规则中,通配符会被自动展开.但在变量的定义和函数引用时,通配符将失效.这种情况下如果需要通配符有效,就需要使用函数“wildcard”,它的 ...

  2. mysql的cast()函数

    先来补个知识: decimal的用法: decimal(18,4)总长18位,包括1位小数点和4为小数,也就是说18-1-4=13整数位只有13位decimal(19,1)总长19位,17位整数,1位 ...

  3. bzoj 4069 [Apio2015]巴厘岛的雕塑 dp

    [Apio2015]巴厘岛的雕塑 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 494  Solved: 238[Submit][Status][Dis ...

  4. HDU3308 线段树(区间合并)

    LCIS Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submis ...

  5. git 回退

    回退命令: $ git reset --hard HEAD^ 回退到上个版本$ git reset --hard HEAD~3 回退到前3次提交之前,以此类推,回退到n次提交之前 $ git rese ...

  6. python---aiohttp的使用

    1.aiohttp的简单使用(配合asyncio模块) import asyncio,aiohttp async def fetch_async(url): print(url) async with ...

  7. mysql连接时权限问题 用户问题

    启动工程时会连接mysql数据库,此时报错: ERROR 1044 (42000): Access denied for user 'pay'@'localhost' to database 'pay ...

  8. [POI2008]BLO-Blockade

    https://www.luogu.org/problem/show?pid=3469 题目描述 There are exactly  towns in Byteotia. Some towns ar ...

  9. K-近邻(KNN)算法

    1,KNN算法对未知类别属性的数据集中的每个点依次执行以下操作: 计算已知类别数据集中的点与当前点之间的距离; 按照距离递增排序; 选取与当前点距离最小的k个点; 确定前k个点所在类别的出现频率; 返 ...

  10. 用Photoshop制作一寸照片

    好了简单介绍一下自己如何制作一寸照片.   工具/原料   Photoshop CS4 更高版本也可以 方法/步骤   1 打开你要修改的照片 2 选择裁剪工具设置参数   选择最佳位置裁剪   选择 ...