LeetCode | 78. 子集
原题(Medium):
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
思路一:回溯
深度优先,从单一数组成员出发,以下标为剪枝条件,即不能搜索时不能小于当前下标,这防止了重复子集的出现,且途中把遇到的元素都加入临时数组,并把临时数组加入解集。
1 void recall(vector<vector<int>> &res, vector<int> &nums, vector<int> tmp, int index){ 2 if(tmp.size()<=nums.size()) 3 res.push_back(tmp); 4 for(int i = index;i<nums.size();i++) //以下标index起始,防止了重复子集的出现 5 { 6 tmp.push_back(nums[i]); //把当前下标的成员加入临时子集,并深度搜索拥有此下标成员后的组合 7 recall(res, nums, tmp, i+1); 8 tmp.pop_back(); //在穷尽数组后,把当前下标成员弹出子集。 9 } 10 } 11 vector<vector<int>> subsets(vector<int>& nums) { 12 vector<vector<int>> res; 13 vector<int> tmp; 14 recall(res, nums, tmp, 0); //从空集出发,这就包括了空集 15 return res; 16 }
思路二:位运算
独热码,获取数组长度,得其数组状态数(有多少状态就有多少子集)subset_sum,在示例中subset_sum就为8(1<<3),用连续的独热码依次标记nums数组的每一个元素,这体现在第二个循环的1<<j里,第0号元素的独热码为001,第1号的独热码为010,第2号的独热码为100。我们能通过单个元素的编码( i<< j)和每个子集的编码( i )进行比较而轻易地知道这个元素在不在这个子集中。例如当 i = 5 时,此时其二进制为101,这代表的子集为{1,3},则只有j为0(1<<0 = 001)或者j为2(1<<2 = 100)时,按位与运算的结果(结果为001和100)才为非0,可把nums[0]与nums[2]加入外循环定义的子集。
1 vector<vector<int>> subsets(vector<int>& nums) { 2 vector<vector<int>>subset_set; 3 int subset_num = 1 << nums.size(); //获取数组长度,得其数组状态数(有多少状态就有多少子集) 4 for (int i = 0; i<subset_num; i++) { 5 vector<int>subset; 6 for (int j = 0; j<nums.size(); j++) { 7 if (i & 1 << j) { //单个元素的编码和每个子集的编码进行按位与运算 8 subset.push_back(nums[j]); 9 } 10 } 11 subset_set.push_back(subset); 12 } 13 return subset_set; 14 }