里程碑:第一百篇文章
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
这个题目很容易想到使用DFS的方式来解决,因为组合的题容易产生转移方程,这样也是没有什么问题的。
对于数组(1,2,3),所有的子集正好是Cn0+Cn1+Cn2+...+Cnn,算出来也就是2的n次方。
凡是和2的n次方扯上关系的算法题,貌似都可以用位运算来试试!
对于数组(1,2,3),如果用位来表示,可以看成是111,而000表示空集,也就是说0表示不存在,1表示存在。
这个解法以前的一篇文章有类似的思路。公众号现在发了一百来篇文章,也忘了是哪篇了。
对于集合中的任意元素x,如果x包含进来,就表示为1,不包含,就表示为0。
而从0到2n-1的每次迭代,都表示一次变化,每一次变化都是集合中的一种。
int** subsets(int* nums, int numsSize, int** columnSizes, int* returnSize) {
*returnSize = pow(2, numsSize);
int **result;
int *colSize;
//分配二级指针,这块内存是一个数组,每个数组元素都是一个一级指针
result = (int**)malloc(sizeof(int*) * (*returnSize));
colSize = (int**)malloc(sizeof(int*) * (*returnSize));
//计算出所有的组合
//所有的组合也就是0-(2^n-1)的位形式
//如3,0-7: [000, 001, 010, 011, 100, 101, 110, 111]
for (int i = 0; i < *returnSize; i++) {
int x = i;
int count = 0;
while (x) {
if (x & 1)
count++;
x >>= 1;
}
colSize[i] = count;
//分配大小,这个for循环的作用仅仅只是分配好大小
result[i] = (int*)malloc(sizeof(int) * (colSize[i]));
}
*columnSizes = colSize;
for (int i = 0; i < *returnSize; i++) {
int x = i;
int col = 0;
int j = 0;
while (x) {
//如果哪一位上是1,就记录一下这个位置
if (x & 1) {
result[i][col++] = nums[j];
}
j++;
x >>= 1;
}
}
return result;
}
int main(int argc, char **argv)
{
int i, j;
if (argc <= 1) {
fprintf(stderr, "Usage: ./test array...\n");
exit(-1);
}
int size = argc - 1;
int *nums = malloc(size * sizeof(int));
for (i = 0; i < size; i++) {
nums[i] = atoi(argv[i + 1]);
}
int *sizes;
int count;
int **lists = subsets(nums, size, &sizes, &count);
for (i = 0; i < count; i++) {
for (j = 0; j < sizes[i]; j++) {
printf("%d ", lists[i][j]);
}
printf("\n");
}
return 0;
}