题目

给定一个包含重复元素的数组 candidates 和一个目标值 target,找出数组中所有不重复的可以使数字和为目标值的组合。数组中的数字只能在每个组合中使用一次。

引言

组合总和 II问题是一个经典的组合求和问题,与组合总和问题类似,但不同之处在于组合中的每个数字只能使用一次,并且候选数组中可能包含重复的元素。在解决这个问题时,我们需要考虑回溯算法和数组元素的组合逻辑。

算法思路

我们将使用深度优先搜索(DFS)的方法来解决组合总和 II问题。

算法的步骤如下:

  1. 首先对候选数组进行排序,这样在后续搜索的过程中可以方便地跳过重复的元素。
  2. 定义一个辅助函数 dfs,用于搜索数组中所有可能的组合。在 dfs 函数中,维护一个当前组合的临时数组 temp,和一个当前组合的数字和 sum
  3. 遍历候选数组中的每个数字,将数字添加到 temp 中,并将 sum 加上该数字。
  4. 如果 sum 等于目标值 target,将当前组合 temp 添加到结果数组中。
  5. 如果 sum 大于目标值 target,说明当前组合已经不满足条件,直接返回。
  6. 否则,递归调用 dfs 函数,继续搜索下一个数字,并更新 tempsum
  7. 每次递归完成后,将 temp 中最后一个数字移除,进行回溯操作。

代码实现

下面是使用C语言实现的代码:

#include <stdio.h>
#include <stdlib.h>

void dfs(int* candidates, int candidatesSize, int target, int* temp, int tempSize, int sum, int** result, int* resultSize, int* colSize, int startIndex) {
    if (sum == target) {
        result[*resultSize] = (int*)malloc(tempSize * sizeof(int));
        for (int i = 0; i < tempSize; i++) {
            result[*resultSize][i] = temp[i];
        }
        colSize[*resultSize] = tempSize;
        (*resultSize)++;
        return;
    }

    for (int i = startIndex; i < candidatesSize; i++) {
        if (i > startIndex && candidates[i] == candidates[i - 1]) {
            continue;
        }
        if (sum + candidates[i] <= target) {
            temp[tempSize] = candidates[i];
            dfs(candidates, candidatesSize, target, temp, tempSize + 1, sum + candidates[i], result, resultSize, colSize, i + 1);
        }
    }
}

int compare(const void* a, const void* b) {
    return (*(int*)a - *(int*)b);
}

int** combinationSum2(int* candidates, int candidatesSize, int target, int* returnSize, int** returnColumnSizes) {
    qsort(candidates, candidatesSize, sizeof(int), compare);

    int** result = (int**)malloc(1000 * sizeof(int*));
    int* colSize = (int*)malloc(1000 * sizeof(int));
    int* temp = (int*)malloc(target * sizeof(int));
    int resultSize = 0;

    dfs(candidates, candidatesSize, target, temp, 0, 0, result, &resultSize, colSize, 0);

    *returnSize = resultSize;
    *returnColumnSizes = colSize;
    return result;
}

int main() {
    int candidates[] = {10, 1, 2, 7, 6, 1, 5};
    int candidatesSize = sizeof(candidates) / sizeof(candidates[0]);
    int target = 8;
    int returnSize;
    int* returnColumnSizes;

    int** result = combinationSum2(candidates, candidatesSize, target, &returnSize, &returnColumnSizes);

    printf("Candidates: ");
    for (int i = 0; i < candidatesSize; i++) {
        printf("%d ", candidates[i]);
    }
    printf("\n");

    printf("Target: %d\n", target);
    printf("Combinations:\n");
    for (int i = 0; i < returnSize; i++) {
        for (int j = 0; j < returnColumnSizes[i]; j++) {
            printf("%d ", result[i][j]);
        }
        printf("\n");
        free(result[i]);
    }

    free(result);
    free(returnColumnSizes);

    return 0;
}

算法分析

该算法使用了深度优先搜索(DFS)搜索数组中所有可能的组合,所以时间复杂度为 O(S),其中 S 是所有可行解的长度之和。在最坏的情况下,可行解的长度为 target 的指数级别,所以时间复杂度为 O(2^n)。

空间复杂度为 O(target),算法使用了临时数组 temp 和结果数组 result。

示例和测试

示例输入:

Candidates: 10 1 2 7 6 1 5
Target: 8

示例输出:

Combinations:
1 1 6 
1 2 5 
1 7 
2 6 

总结

本文使用C语言实现了解答组合总和 II问题的代码。通过使用深度优先搜索(DFS)搜索数组中所有可能的组合,我们能够找到数组中所有不重复的可以使数字和为目标值的组合。该算法的时间复杂度为 O(2^n),空间复杂度为 O(target)。

标签: 编程算法, 编程算法题, 编程算法大全, 编程算法流程, 算法设计与分析, 数据结构与算法, 算法优化, 算法实现, 常见编程算法, 编程算法入门, 编程算法进阶, 编程算法精通