力扣78题详解:C语言实现子集问题
题目描述
给定一个不含重复元素的整数数组 nums
,返回其所有可能的子集(幂集)。
说明:解集不能包含重复的子集,顺序无关。
示例
输入:nums = [1,2,3]
输出:
[
[],
[1],
[2],
[3],
[1,2],
[1,3],
[2,3],
[1,2,3]
]
解题思路
1. 子集的总数
对于包含
n
n
n 个元素的数组,其所有子集的数量为
2
n
2^n
2n。
例如,数组 [1, 2, 3]
的子集总数为
2
3
=
8
2^3 = 8
23=8。
2. 回溯法
采用回溯法逐步构建子集:
- 每次递归调用选择是否将当前数字加入子集中。
- 遍历所有可能的选择。
代码实现
以下是C语言的完整实现:
#include <stdio.h>
#include <stdlib.h>
void backtrack(int* nums, int numsSize, int** result, int* returnColumnSizes, int* subset, int subsetSize, int start, int* returnSize) {
// 创建一个当前子集的副本
result[*returnSize] = (int*)malloc(subsetSize * sizeof(int));
for (int i = 0; i < subsetSize; i++) {
result[*returnSize][i] = subset[i];
}
returnColumnSizes[*returnSize] = subsetSize;
(*returnSize)++;
// 继续生成子集
for (int i = start; i < numsSize; i++) {
subset[subsetSize] = nums[i];
backtrack(nums, numsSize, result, returnColumnSizes, subset, subsetSize + 1, i + 1, returnSize);
}
}
int** subsets(int* nums, int numsSize, int* returnSize, int** returnColumnSizes) {
int maxSubsets = 1 << numsSize; // 子集总数是 2^n
int** result = (int**)malloc(maxSubsets * sizeof(int*));
*returnColumnSizes = (int*)malloc(maxSubsets * sizeof(int));
*returnSize = 0;
int* subset = (int*)malloc(numsSize * sizeof(int)); // 临时存储一个子集
backtrack(nums, numsSize, result, *returnColumnSizes, subset, 0, 0, returnSize);
free(subset);
return result;
}
int main() {
int nums[] = {1, 2, 3};
int numsSize = sizeof(nums) / sizeof(nums[0]);
int returnSize;
int* returnColumnSizes;
int** result = subsets(nums, numsSize, &returnSize, &returnColumnSizes);
// 打印结果
printf("[\n");
for (int i = 0; i < returnSize; i++) {
printf(" [");
for (int j = 0; j < returnColumnSizes[i]; j++) {
printf("%d", result[i][j]);
if (j < returnColumnSizes[i] - 1) printf(", ");
}
printf("]");
if (i < returnSize - 1) printf(",");
printf("\n");
}
printf("]\n");
// 释放内存
for (int i = 0; i < returnSize; i++) {
free(result[i]);
}
free(result);
free(returnColumnSizes);
return 0;
}
代码详解
1. 回溯函数 backtrack
void backtrack(int* nums, int numsSize, int** result, int* returnColumnSizes, int* subset, int subsetSize, int start, int* returnSize)
参数解析:
nums
: 输入数组。numsSize
: 数组的长度。result
: 存储所有子集的数组。returnColumnSizes
: 每个子集的长度。subset
: 当前子集的临时存储。subsetSize
: 当前子集的大小。start
: 开始选择元素的索引。returnSize
: 当前子集的总数。
核心逻辑:
-
保存当前子集:
result[*returnSize] = (int*)malloc(subsetSize * sizeof(int)); for (int i = 0; i < subsetSize; i++) { result[*returnSize][i] = subset[i]; } returnColumnSizes[*returnSize] = subsetSize; (*returnSize)++;
-
递归选择下一个元素:
for (int i = start; i < numsSize; i++) { subset[subsetSize] = nums[i]; backtrack(nums, numsSize, result, returnColumnSizes, subset, subsetSize + 1, i + 1, returnSize); }
2. 主函数 subsets
-
计算最大子集数量:
使用位运算 2 n 2^n 2n:int maxSubsets = 1 << numsSize;
-
动态分配存储空间:
int** result = (int**)malloc(maxSubsets * sizeof(int*)); *returnColumnSizes = (int*)malloc(maxSubsets * sizeof(int));
-
调用回溯函数:
backtrack(nums, numsSize, result, *returnColumnSizes, subset, 0, 0, returnSize);
输出示例
运行程序,输入 nums = [1, 2, 3]
,输出:
[
[],
[1],
[2],
[3],
[1, 2],
[1, 3],
[2, 3],
[1, 2, 3]
]