代码随想录算法训练营第二十五天 | 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循环结束。

参考代码

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取完所有的数字(代表递归的次数,也是树的深度)。

参考代码

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;
}
  • 19
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值