代码随想录算法训练营第二十五天 | 216.组合总和III,17.电话号码的字母组合 [回溯篇]

代码随想录算法训练营第二十五天


LeetCode 216.组合总和III

题目链接:216.组合总和III

文章讲解:代码随想录#216.组合总和III

视频讲解:回溯算法如何剪枝?| LeetCode:216.组合总和III

题目描述

找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:

  • 只使用数字1到9
  • 每个数字最多 使用一次
    返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。

示例1

输入:k = 3, n = 7

输出: [[1,2,4]]

解释:

1 + 2 + 4 = 7

没有其他符合的组合了。

示例2

输入:k = 3, n = 9

输出: [[1,2,6], [1,3,5], [2,3,4]]

解释:

1 + 2 + 6 = 9

1 + 3 + 5 = 9

2 + 3 + 4 = 9

没有其他符合的组合了。

提示

  • 2 <= k <= 9
  • 1 <= n <= 60

思路

在数字1到9之间找到k个数,其和为n,并且保证每个数字只能使用一次。

如果使用k个for循环遍历数字,明显不太合适,所以此题应使用回溯法来求解。

k表示递归的次数,也就是树的深度。

数字1到9表示每次遍历树的宽度,题中要求每个数字只使用一次,所以每次递归时需要考虑遍历1到9的索引。

回溯的终止条条件为数的和为n。

在遍历的过种中也可以考虑如下的剪枝操作:

①当数和已经大于n了,退出当前循环。

②当剩余的数字少于还需要的数字个数时,for循环结束。

参考代码

c 复制代码
int sum, cnt;
int** res;
typedef struct {
    int index;
    int nums[10];
} Data;

Data data = {0};

void backtracking(int k, int n, int idx) {
    if (data.index == k) {
        if (sum == n) { // 符合条件,记录数据
            res[cnt] = malloc(k * sizeof(int));
            for (int i = 0; i < k; i++)
                res[cnt][i] = data.nums[i];
            cnt++;
        }
        return;
    }

	// 剪枝二:保证剩余数字个数大于等于还需要的数字个数
    for (int i = idx; i <= 9 - (k - data.index) + 1; i++) {
        if (sum + i > n)
            break; // 剪枝一:如果sum+i大于n,直接跳出for循环
        sum += i;
        data.nums[data.index++] = i;
        backtracking(k, n, i + 1);
        sum -= i; // 回溯
        data.index--;
        data.nums[data.index] = 0;
    }
}

int** combinationSum3(int k, int n, int* returnSize, int** returnColumnSizes) {
    res = (int**)malloc(100 * sizeof(int*));
    sum = 0;
    cnt = 0;

    backtracking(k, n, 1);

    *returnSize = cnt;
    *returnColumnSizes =
        (int*)malloc(sizeof(int) * cnt); // 需要给returnColumnSizes分配内存
    for (int i = 0; i < cnt; i++) {
        (*returnColumnSizes)[i] = k;
    }
    return res;
}

总结

  1. 这道题相对 LeetCode 77.组合 多了一个条件,需要找出和为n的k个数的组合,整个集合是固定的,相对来说也比较简单。

LeetCode 17.电话号码的字母组合

题目链接:17.电话号码的字母组合

文章讲解:代码随想录#17.电话号码的字母组合

视频讲解:还得用回溯算法!| LeetCode:17.电话号码的字母组合

题目描述

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

示例1

输入:digits = "23"

输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]

示例2

输入:digits = ""

输出:[]

示例3

输入:digits = "2"

输出:["a","b","c"]

提示

  • 0 <= digits.length <= 4
  • digits[i] 是范围 ['2', '9'] 的一个数字。

思路

这道题关键是需要构建一个数字与字母转换的表,如:

2\] = \["abc"

3\] = \["def"

...

对于示例1来说,输入了两个数字2和3,树的深度为2。

首先遍历数字2,从转换表中取出字符串"abc",

然后先遍历a,将a存起来,(第一层)

调用递归函数,在递归函数中,遍历数字3...(第二层)

退出递归函数后,进行回溯操作,

接着再遍历b,将b存起来,

...

直到遍历完c后(遍历树的宽度)。

可以构建一个递归函数,返回值为void,参数有两个人,一个是输入的字符串,一个是当前遍历层级。

递归函数的终止条件是digits取完所有的数字(代表递归的次数,也是树的深度)。

参考代码

c 复制代码
char **res;
int cnt;
typedef struct {
    int index;
    char str[5];
}Data;
Data data = {0};

char *convert[] = {
    "", // 0
    "", // 1
    "abc", // 2
    "def", // 3
    "ghi", // 4
    "jkl", // 5
    "mno", // 6
    "pqrs", // 7
    "tuv", // 8
    "wxyz", // 9
};

void backtracking(char *src, int level)
{
    if (strlen(src) == level) {
        res[cnt] = (char*)malloc(sizeof(char) * (data.index + 1));        
        strcpy(res[cnt++], data.str);
        return; 
    }

    char *s = convert[src[level] - '0'];
    int len = strlen(s);
    for (int i = 0; i < len; i++) {
        data.str[data.index++] = s[i];
        backtracking(src, level + 1);
        data.index--; // 回溯
        data.str[data.index] = '\0';
    }
}

char** letterCombinations(char* digits, int* returnSize) {
    if (strlen(digits) == 0) {
        *returnSize = 0;
        return NULL;
    }

    res = (char**)malloc(1000 * sizeof(char*));
    cnt = 0;    

    backtracking(digits, 0);
    *returnSize = cnt;
    return res;
}
相关推荐
纪元A梦1 小时前
贪心算法应用:化工反应器调度问题详解
算法·贪心算法
阿让啊2 小时前
C语言strtol 函数使用方法
c语言·数据结构·c++·单片机·嵌入式硬件
深圳市快瞳科技有限公司2 小时前
小场景大市场:猫狗识别算法在宠物智能设备中的应用
算法·计算机视觉·宠物
liulilittle2 小时前
OPENPPP2 —— IP标准校验和算法深度剖析:从原理到SSE2优化实现
网络·c++·网络协议·tcp/ip·算法·ip·通信
superlls5 小时前
(算法 哈希表)【LeetCode 349】两个数组的交集 思路笔记自留
java·数据结构·算法
田里的水稻5 小时前
C++_队列编码实例,从末端添加对象,同时把头部的对象剔除掉,中的队列长度为设置长度NUM_OBJ
java·c++·算法
纪元A梦5 小时前
贪心算法应用:保险理赔调度问题详解
算法·贪心算法
Florence235 小时前
计算机组成原理:GPU架构、并行计算、内存层次结构等
c语言
Ripple123126 小时前
数据结构:顺序表与链表
数据结构·链表
Jayden_Ruan6 小时前
C++逆向输出一个字符串(三)
开发语言·c++·算法