力扣第 77 题 组合

题目描述

给定两个整数 nk,返回范围 [1, n] 中所有可能的 k 个数的组合。

  • 你可以按任意顺序返回答案。

示例

示例 1

输入

plaintext 复制代码
n = 4, k = 2

输出

plaintext 复制代码
[[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]
示例 2

输入

plaintext 复制代码
n = 1, k = 1

输出

plaintext 复制代码
[[1]]

解题思路

1. 回溯法

回溯法是解决组合问题的经典方法,通过递归构建所有可能的组合。

算法步骤

  1. 定义一个函数 backtrack(start, path),其中 start 表示搜索的起点,path 是当前构建的组合。
  2. 如果当前组合 path 的长度等于 k,将其加入结果集中。
  3. 遍历从 startn 的所有数字:
    • 将当前数字加入组合。
    • 递归构建下一个数字的组合。
    • 回溯,移除当前数字。

回溯法的时间复杂度是 O(C(n, k)) ,其中 C ( n , k ) = n ! k ! ( n − k ) ! C(n, k) = \frac{n!}{k!(n-k)!} C(n,k)=k!(n−k)!n!。


实现代码

C语言实现
c 复制代码
#include <stdio.h>
#include <stdlib.h>

// 动态数组结构
typedef struct {
    int** data;
    int size;
    int capacity;
} Array;

void initArray(Array* arr, int capacity) {
    arr->data = (int**)malloc(sizeof(int*) * capacity);
    arr->size = 0;
    arr->capacity = capacity;
}

void addToArray(Array* arr, int* combination, int k) {
    if (arr->size == arr->capacity) {
        arr->capacity *= 2;
        arr->data = (int**)realloc(arr->data, sizeof(int*) * arr->capacity);
    }
    arr->data[arr->size] = (int*)malloc(sizeof(int) * k);
    for (int i = 0; i < k; i++) {
        arr->data[arr->size][i] = combination[i];
    }
    arr->size++;
}

void backtrack(int n, int k, int start, int* combination, int combSize, Array* result) {
    if (combSize == k) {
        addToArray(result, combination, k);
        return;
    }
    for (int i = start; i <= n; i++) {
        combination[combSize] = i;
        backtrack(n, k, i + 1, combination, combSize + 1, result);
    }
}

int** combine(int n, int k, int* returnSize, int** returnColumnSizes) {
    Array result;
    initArray(&result, 16);
    int* combination = (int*)malloc(sizeof(int) * k);
    backtrack(n, k, 1, combination, 0, &result);

    *returnSize = result.size;
    *returnColumnSizes = (int*)malloc(sizeof(int) * result.size);
    for (int i = 0; i < result.size; i++) {
        (*returnColumnSizes)[i] = k;
    }

    free(combination);
    return result.data;
}

int main() {
    int n = 4, k = 2;
    int returnSize;
    int* returnColumnSizes;

    int** combinations = combine(n, k, &returnSize, &returnColumnSizes);

    printf("Combinations:\n");
    for (int i = 0; i < returnSize; i++) {
        printf("[");
        for (int j = 0; j < returnColumnSizes[i]; j++) {
            printf("%d", combinations[i][j]);
            if (j < returnColumnSizes[i] - 1) printf(", ");
        }
        printf("]\n");
        free(combinations[i]); // 释放每个组合的内存
    }

    free(combinations); // 释放结果数组的内存
    free(returnColumnSizes); // 释放列大小数组的内存

    return 0;
}

代码解析

  1. 动态数组

    • 使用 Array 结构来动态存储组合结果。
    • initArray 初始化数组,addToArray 动态增加组合。
  2. 回溯函数

    • backtrack 函数递归构建所有可能的组合。
    • 使用 start 控制数字范围,避免重复组合。
  3. 主函数

    • combine 是主函数,调用回溯并返回结果。
    • 动态分配 returnColumnSizes 以存储每个组合的列数。
  4. 内存管理

    • 在主函数中释放动态分配的内存,避免内存泄漏。

时间复杂度和空间复杂度

  • 时间复杂度

    • 回溯构造所有组合的复杂度是 O(C(n, k)) ,即 n ! k ! ( n − k ) ! \frac{n!}{k!(n-k)!} k!(n−k)!n!。
  • 空间复杂度

    • 临时数组 combination 的空间复杂度为 O(k)
    • 存储结果的空间复杂度为 O ( C ( n , k ) ⋅ k ) O(C(n, k) \cdot k) O(C(n,k)⋅k)
相关推荐
你好~每一天6 小时前
数据分析专员:当传统汽车销售融入AI智能,如何驱动业绩新增长
大数据·数据结构·人工智能·学习·数据分析·汽车·高性价比
再__努力1点7 小时前
【76】Haar特征的Adaboost级联人脸检测全解析及python实现
开发语言·图像处理·人工智能·python·算法·计算机视觉·人脸检测
溟洵7 小时前
【算法C++】链表(题目列表:两数相加、两两交换链表中的节点、重排链表、合并 K 个升序链表、K 个一组翻转链表7)
数据结构·c++·算法·链表
_OP_CHEN7 小时前
【C++数据结构进阶】玩转并查集:从原理到实战,C++ 实现与高频面试题全解析
数据结构·c++·算法
gugugu.7 小时前
算法:hot100---128. 最长连续序列
算法
天呐草莓7 小时前
支持向量机(SVM)
人工智能·python·算法·机器学习·支持向量机·数据挖掘·数据分析
zore_c7 小时前
【数据结构】队列——超详解!!!(包含队列的实现)
c语言·网络·数据结构·c++·笔记·算法·链表
小杰帅气7 小时前
智能指针喵喵喵
开发语言·c++·算法
智驱力人工智能7 小时前
守护生命的水上之眼 无人机人员落水检测系统的技术攻坚与应用实践 无人机溺水识别 山区水库无人机落水检测系统 水域安全无人机部署指南
大数据·人工智能·算法·安全·无人机·边缘计算
hweiyu007 小时前
排序算法选型决策树
算法·排序算法