一:回溯法理论基础
递归进化,请叫【回溯】算法!
递归:关注代码实现
回溯:关注问题解决
「回溯是递归的副产品,只要有递归就会有回溯」
回溯法也可以叫做回溯搜索法,它是一种搜索的方式
回溯法就是暴力搜索,并不是什么高效的算法,最多再剪枝一下
回溯法------> 深度优先搜索(dfs)
「组合是不强调元素顺序的,排列是强调元素顺序」
回溯算法能解决如下问题:
- 组合问题:N个数里面按一定规则找出k个数的集合
- 排列问题:N个数按一定规则全排列,有几种排列方式
- 切割问题:一个字符串按一定规则有几种切割方式
- 子集问题:一个N个数的集合里有多少符合条件的子集
- 棋盘问题:N皇后,解数独等等
回溯法解决的都是在集合中递归查找子集「集合的大小就构成了树的宽度,递归的深度就构成了树的深度」
回溯算法设计心法:
1. 脑中浮现:问题状态搜索树
2. 切勿妄想一步到位:先实现,再优化
3. 搜索剪枝记心尖:无招胜有招
回溯法模板:
1. 递归函数(参数和返回值) 2. 确定终止条件 3. 单层递归逻辑 // 一定要分成横纵两个方面思考回溯 void dfs(参数) { if (终止条件) { 存放结果; return; } for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) { 处理节点; DFS(路径,选择列表); // 递归 回溯,撤销处理结果 } }
二:习题
1. ✔组合
代码实现
方法一:定义全局变量
/** * Return an array of arrays of size *returnSize. * The sizes of the arrays are returned as *returnColumnSizes array. * Note: Both returned array and *columnSizes array must be malloced, assume caller calls free(). */ int *path; int pathSize; int **res; int resSize; void dfs(int startindex, int n, int k) { if (pathSize == k) { int *temp = malloc(sizeof(int) * k); for (int i = 0; i < k; i++) { temp[i] = path[i]; } res[resSize++] = temp; return; } // 剪枝优化 if (n - startindex + 1 + pathSize < k) { return; } for (int i = startindex; i <= n; i++) { path[pathSize++] = i; dfs(i + 1, n, k); pathSize--; } } int** combine(int n, int k, int *returnSize, int **returnColumnSizes) { path = malloc(sizeof(int) * k); res = malloc(sizeof(int*) * 200001); pathSize = resSize = 0; dfs(1, n, k); *returnSize = resSize; *returnColumnSizes = malloc(sizeof(int) * resSize); for (int i = 0; i < resSize; i++) { (*returnColumnSizes)[i] = k; } return res; }
方法一:定义局部变量
/** * Return an array of arrays of size *returnSize. * The sizes of the arrays are returned as *returnColumnSizes array. * Note: Both returned array and *columnSizes array must be malloced, assume caller calls free(). */ void dfs(int startindex, int n, int **res, int *resSize, int *path, int *pathSize, int k) { if (*pathSize == k) { int *temp = malloc(sizeof(int) * k); for (int i = 0; i < k; i++) { temp[i] = path[i]; } res[(*resSize)++] = temp; return; } // 剪枝优化 if (n - startindex + 1 + *pathSize < k) { return; } for (int i = startindex; i <= n; i++) { path[(*pathSize)++] = i; dfs(i + 1, n, res, resSize, path, pathSize, k); (*pathSize)--; } } int** combine(int n, int k, int *returnSize, int **returnColumnSizes) { int **res = malloc(sizeof(int*) * 1000000); int resSize = 0; int *path = malloc(sizeof(int) * k); int pathSize = 0; dfs(1, n, res, &resSize, path, &pathSize, k); *returnSize = resSize; *returnColumnSizes = malloc(sizeof(int) * resSize); for (int i = 0; i < resSize; i++) { (*returnColumnSizes)[i] = k; } return res; }
2. ✔组合总和 ||| (不可以重复选取自己,数组中没有重复元素)
/** * Return an array of arrays of size *returnSize. * The sizes of the arrays are returned as *returnColumnSizes array. * Note: Both returned array and *columnSizes array must be malloced, assume caller calls free(). */ int *path; int pathSize; int **res; int resSize; // 求和函数 int pathsum(int n) { int sum = 0; for (int i = 0; i < n; i++) { sum = sum + path[i]; } return sum; } void dfs(int targetsum, int k, int startindex) { // 剪枝优化 if (pathSize + (9 - startindex + 1) < k) { return; } // 满足条件 if (pathSize == k) { if (pathsum(k) == targetsum) { int *temp = malloc(sizeof(int) * k); for (int i = 0; i < k; i++) { temp[i] = path[i]; } res[resSize++] = temp; } return; } // 递归回溯 for (int i = startindex; i <= 9; i++) { path[pathSize++] = i; dfs(targetsum, k, i + 1); pathSize--; } } int** combinationSum3(int k, int n, int *returnSize, int **returnColumnSizes) { path = (int*)malloc(sizeof(int) * 9); res = (int**)malloc(sizeof(int*) * 2001); resSize = pathSize = 0; // 测试时会多次调用这个函数,所以每次调用都要重新归零 dfs(n, k, 1); // 分配返回数组的行和列 *returnSize = resSize; *returnColumnSizes = malloc(sizeof(int) * resSize); for (int i = 0; i < resSize; i++) { (*returnColumnSizes)[i] = k; } return res; }
3. 😭电话号码的字母组合
代码实现:
#define NUM_MAX 256 // 4*4*4*4=256 // 转化电话按键 char word[10][5] = { "\0", // 0 "\0", // 1 "abc", // 2 "def", // 3 "ghi", // 4 "jkl", // 5 "mno", // 6 "pqrs", // 7 "tuv", // 8 "wxyz" // 9 }; void dfs(int len, int *pathSize, char *digits, char **res, char *path, int *returnSize) { // 如果满足条件,添加字母组合 if (*pathSize == len) { res[*returnSize] = (char*)malloc(len + 1); // 注意:len必须+1 用于存储最后一个'\0' memset(res[*returnSize], 0, sizeof(char) * (len + 1)); strcpy(res[*returnSize], path); (*returnSize)++; return; } // 可选择的列表 for (int i = 0; i < strlen(word[digits[*pathSize] - '0']); i++) { // 做选择 path[(*pathSize)] = word[digits[*pathSize] - '0'][i]; (*pathSize)++; dfs(len, pathSize, digits, res, path, returnSize); // 回溯 (*pathSize)--; } } /** * Note: The returned array must be malloced, assume caller calls free(). */ char** letterCombinations(char *digits, int *returnSize) { // 特殊用例1 if (digits == NULL) { *returnSize = 0; return NULL; } // 特殊用例2 int len = strlen(digits); if (len == 0) { *returnSize = 0; return NULL; } // 存储结果分配内存 char **res = (char**)malloc(NUM_MAX * sizeof(char*)); // 临时字母组合 char *path = (char*)malloc((len + 1) * sizeof(char)); // 注意:len必须+1,最后一个存放'\0' memset(path, 0, (len + 1) * sizeof(char)); int pathSize = 0; *returnSize = 0; dfs(len, &pathSize, digits, res, path, returnSize); return res; }
4. ✔组合总和(可以重复选取自己,数组中没有重复元素)
/** * Return an array of arrays of size *returnSize. * The sizes of the arrays are returned as *returnColumnSizes array. * Note: Both returned array and *columnSizes array must be malloced, assume caller calls free(). */ int *path; int pathSize; int **res; int resSize; int *length; void dfs(int *candidates, int candidatesSize, int target, int sum , int startindex) { if (sum == target) { int *temp = malloc(sizeof(int) * pathSize); for (int i = 0; i < pathSize; i++) { temp[i] = path[i]; } res[resSize] = temp; length[resSize++] = pathSize; return; } // 优化:sum < target:因为 2 <= candidates[i] <= 40 // 注意:不能写成 sum + candidates[i] <= target // 会导致candidates[i]没记录到path数组中,把path数组中的数回溯了 for (int i = startindex; i < candidatesSize && sum < target; i++) { sum += candidates[i]; path[pathSize++] = candidates[i]; // 递归 dfs(candidates, candidatesSize, target, sum, i); // 回溯 sum -= candidates[i]; pathSize--; } } int** combinationSum(int *candidates, int candidatesSize, int target, int *returnSize, int **returnColumnSizes){ pathSize = resSize = 0; path = malloc(sizeof(int) * 100); res = malloc(sizeof(int*) * 500); length = malloc(sizeof(int) * 500); dfs(candidates, candidatesSize, target, 0, 0); *returnSize = resSize; *returnColumnSizes = malloc(sizeof(int) *resSize); for (int i = 0 ; i < resSize; i++){ (*returnColumnSizes)[i] = length[i]; } return res; }
5. ✔组合总和||(不可以重复选取自己,数组中有重复元素)
/* arr[10, 1, 2, 7, 6, 1, 5] targetSum = 8 [1、1、6] [1、2、5] [1、7] [2、6] 1、将数组排序 2、树层去重 和 树枝不去重 */ /** * Return an array of arrays of size *returnSize. * The sizes of the arrays are returned as *returnColumnSizes array. * Note: Both returned array and *columnSizes array must be malloced, assume caller calls free(). */ int *path; int pathSize; int **res; int resSize; int *length; int *used; void dfs(int *candidates, int candidatesSize, int target, int sum , int startindex) { if (sum == target) { int *temp = malloc(sizeof(int) * pathSize); for (int i = 0; i < pathSize; i++) { temp[i] = path[i]; } res[resSize] = temp; length[resSize++] = pathSize; return; } for (int i = startindex; i < candidatesSize && sum < target; i++) { if (i > 0 && candidates[i] == candidates[i - 1] && used[i - 1] == 0) { // 树层去重 continue; } sum += candidates[i]; path[pathSize++] = candidates[i]; used[i] = 1; dfs(candidates, candidatesSize, target, sum, i + 1); sum -= candidates[i]; pathSize--; used[i] = 0; } } // 冒泡排序 void bubble_sort(int *arr, int l, int n) { // l:已排好序的元素个数 n:元素总个数 int flag = 1; // 标记位优化 当序列在找到所有的最大值之前就已经将序列排好序了,直接结束循环 for (int i = n - 1; i >= l + 1 && flag; i--) { // i决定了哪一轮冒泡 flag = 0; for (int j = l; j < i; j++) { // j决定哪两个元素进行比较 if (arr[j] > arr[j + 1]) { flag = 1; int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } } int** combinationSum2(int *candidates, int candidatesSize, int target, int *returnSize, int **returnColumnSizes){ pathSize = resSize = 0; path = malloc(sizeof(int) * candidatesSize); res = malloc(sizeof(int*) * 500); length = malloc(sizeof(int) * 500); used = malloc(sizeof(int) * candidatesSize); // memset(used, 0, sizeof(used)); // 错误 因为used是一个指针,sizeof(used) == 8字节 memset(used, 0, sizeof(int) * candidatesSize); bubble_sort(candidates, 0, candidatesSize); dfs(candidates, candidatesSize, target, 0, 0); *returnSize = resSize; *returnColumnSizes = malloc(sizeof(int) *resSize); for (int i = 0 ; i < resSize; i++){ returnColumnSizes[0][i] = length[i]; } return res; }
6. 😭分割回文串
重点:每个子串范围 [startindex, i]
代码实现:
/** * Return an array of arrays of size *returnSize. * The sizes of the arrays are returned as *returnColumnSizes array. * Note: Both returned array and *columnSizes array must be malloced, assume caller calls free(). */ // 判断是否为回文串 bool ifstr(char *s, int i, int j) { while (i < j) { if (s[i] != s[j]) { return false; } i++; j--; } return true; } // 递归回溯枚举子串 void dfs(char *s, char ***res, int *returnSize, int **returnColumnSizes, int len, char **path, int pathSize, int startindex) { if (startindex == len) { // 切割到最后了,保存有效组合 res[(*returnSize)] = (char**)malloc(sizeof(char*) * pathSize); for (int i = 0; i < pathSize; i++) { res[(*returnSize)][i] = (char*)malloc(sizeof(char) * (strlen(path[i]) + 1)); strcpy(res[(*returnSize)][i], path[i]); } (*returnColumnSizes)[(*returnSize)++] = pathSize; return; } for (int i = startindex; i < len; i++) { if (ifstr(s, startindex, i) == false) { // 剪枝,当前不满足切割条件,就没必要在枚举了 continue; } strncpy(path[pathSize], s + startindex, i - startindex + 1); // 满足切割条件,保存子串 path[pathSize][i - startindex + 1] = '\0'; dfs(s, res, returnSize, returnColumnSizes, len, path, pathSize + 1, i + 1); // 递归重复之前动作 } } char*** partition(char *s, int *returnSize, int **returnColumnSizes) { char ***res = (char***)malloc(sizeof(int**) * 50000); *returnColumnSizes = (int*)malloc(sizeof(int) * 50000); int len = strlen(s); char **path = (char**)malloc(sizeof(char*) * 50000); // 保存符合条件的每一个子串 for (int i = 0; i < 5000; i++) { path[i] = (char*)malloc(sizeof(char) * (len + 1)); } *returnSize = 0; // 初始化变量 dfs(s, res, returnSize, returnColumnSizes, len, path, 0, 0); return res; }
7. 复原IP地址
代码实现:
8. 👌子集(不可以重复选取自己,数组中没有重复元素)
(边递归,边存值)
「在树形结构中子集问题是要收集所有节点的结果,而组合问题是收集叶子节点的结果」
/** * Return an array of arrays of size *returnSize. * The sizes of the arrays are returned as *returnColumnSizes array. * Note: Both returned array and *columnSizes array must be malloced, assume caller calls free(). */ void dfs(int *nums, int numsSize, int **res, int *returnSize, int **returnColumnSizes, int *path, int pathSize, int startIndex) { for (int i = startIndex; i < numsSize; i++) { path[pathSize] = nums[i]; pathSize++; res[*returnSize] = (int*)malloc(sizeof(int) * pathSize); // memcpy(res[*returnSize], path, sizeof(int) * pathSize); for (int i = 0; i < pathSize; i++) { res[*returnSize][i] = path[i]; } (*returnColumnSizes)[*returnSize] = pathSize; (*returnSize)++; dfs(nums, numsSize, res, returnSize, returnColumnSizes, path, pathSize, i + 1); pathSize--; } } int** subsets(int *nums, int numsSize, int *returnSize, int **returnColumnSizes) { *returnSize = 0; *returnColumnSizes = (int*)malloc(sizeof(int) * 10001); int **res = (int**)malloc(sizeof(int*) * 10001); int *path = (int*)malloc(sizeof(int) * numsSize); dfs(nums, numsSize, res, returnSize, returnColumnSizes, path, 0, 0); res[*returnSize] = (int*)malloc(sizeof(int) * 0); (*returnColumnSizes)[*returnSize] = 0; (*returnSize)++; return res; }
/** * Return an array of arrays of size *returnSize. * The sizes of the arrays are returned as *returnColumnSizes array. * Note: Both returned array and *columnSizes array must be malloced, assume caller calls free(). */ void dfs(int *nums, int numsSize, int **res, int *returnSize, int **returnColumnSizes, int *path, int pathSize, int startIndex) { res[*returnSize] = (int*)malloc(sizeof(int) * pathSize); // memcpy(res[*returnSize], path, sizeof(int) * pathSize); for (int i = 0; i < pathSize; i++) { res[*returnSize][i] = path[i]; } (*returnColumnSizes)[*returnSize] = pathSize; (*returnSize)++; for (int i = startIndex; i < numsSize; i++) { path[pathSize] = nums[i]; dfs(nums, numsSize, res, returnSize, returnColumnSizes, path, pathSize + 1, i + 1); } } int** subsets(int *nums, int numsSize, int *returnSize, int **returnColumnSizes) { *returnSize = 0; *returnColumnSizes = (int*)malloc(sizeof(int) * 10001); int **res = (int**)malloc(sizeof(int*) * 10001); int *path = (int*)malloc(sizeof(int) * numsSize); dfs(nums, numsSize, res, returnSize, returnColumnSizes, path, 0, 0); return res; }
9. 👌子集||(不可以重复选取自己,数组中有重复元素)
/* arr[1、2、2] [1] [1、2] [1、2、2] [2] [2、2] [] 1、将数组排序 2、树层去重 和 树枝不去重 */ /** * Return an array of arrays of size *returnSize. * The sizes of the arrays are returned as *returnColumnSizes array. * Note: Both returned array and *columnSizes array must be malloced, assume caller calls free(). */ void dfs(int *nums, int numsSize, int **res, int *returnSize, int **returnColumnSizes, int *path, int *used, int pathSize, int startIndex) { res[*returnSize] = (int*)malloc(sizeof(int) * pathSize); // memcpy(res[*returnSize], path, sizeof(int) * pathSize); for (int i = 0; i < pathSize; i++) { res[*returnSize][i] = path[i]; } (*returnColumnSizes)[*returnSize] = pathSize; (*returnSize)++; for (int i = startIndex; i < numsSize; i++) { if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == 0) { // 树层去重 continue; } path[pathSize] = nums[i]; used[i] = 1; dfs(nums, numsSize, res, returnSize, returnColumnSizes, path, used, pathSize + 1, i + 1); used[i] = 0; } } // 冒泡排序 void bubble_sort(int *arr, int l, int n) { // l:已排好序的元素个数 n:元素总个数 int flag = 1; // 标记位优化 当序列在找到所有的最大值之前就已经将序列排好序了,直接结束循环 for (int i = n - 1; i >= l + 1 && flag; i--) { // i决定了哪一轮冒泡 flag = 0; for (int j = l; j < i; j++) { // j决定哪两个元素进行比较 if (arr[j] > arr[j + 1]) { flag = 1; int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } } int** subsetsWithDup(int *nums, int numsSize, int *returnSize, int **returnColumnSizes) { bubble_sort(nums, 0, numsSize); *returnSize = 0; *returnColumnSizes = (int*)malloc(sizeof(int) * 10001); int **res = (int**)malloc(sizeof(int*) * 10001); int *path = (int*)malloc(sizeof(int) * numsSize); int used[numsSize]; memset(used, 0, sizeof(used)); dfs(nums, numsSize, res, returnSize, returnColumnSizes, path, used, 0, 0); return res; }
10. 👌非递减子序列(不可以重复选取自己,数组中有重复元素)
代码实现:
/** * Return an array of arrays of size *returnSize. * The sizes of the arrays are returned as *returnColumnSizes array. * Note: Both returned array and *columnSizes array must be malloced, assume caller calls free(). */ void dfs(int* nums, int numsSize, int **res, int *returnSize, int **returnColumnSizes, int *path, int pathSize, int startIndex) { // 终止条件,元素个数大于等于2个 if (pathSize >= 2) { // 为当前返回值分配空间 res[*returnSize] = (int*)malloc(sizeof(int) * pathSize); // 赋值 memcpy(res[*returnSize], path, sizeof(int) * pathSize); // 当前返回值的列数 (*returnColumnSizes)[*returnSize] = pathSize; // 返回行数加一 (*returnSize)++; } // 当前层使用过的元素,每一层都有会有一个这样的used数组 int used[201] = {0}; for (int i = startIndex; i < numsSize; i++) { if ((pathSize > 0 && nums[i] < path[pathSize - 1]) || used[nums[i] + 100] == 1) { // 树层去重 continue; } // 记录当路径数组中 path[pathSize] = nums[i]; used[nums[i] + 100] = 1; dfs(nums, numsSize, res, returnSize, returnColumnSizes, path, pathSize + 1, i + 1); } } int** findSubsequences(int *nums, int numsSize, int *returnSize, int **returnColumnSizes) { // 返回数组行数 *returnSize = 0; // 特判,递增子序列最少需要两个元素 if (numsSize < 2) { return NULL; } // 返回数组中每一行的列数 *returnColumnSizes = (int*)malloc(sizeof(int) * 100000); // 返回数组 int **res = (int**)malloc(sizeof(int*) * 100000); // 当前路径数组 int path[numsSize]; dfs(nums, numsSize, res, returnSize, returnColumnSizes, path, 0, 0); return res; }
11. 👌全排列(不可以重复选取自己,数组中没有重复元素)
- 每层都是从0开始搜索而不是startIndex
- 需要used数组记录path里都放了哪些元素了
/** * Return an array of arrays of size *returnSize. * The sizes of the arrays are returned as *returnColumnSizes array. * Note: Both returned array and *columnSizes array must be malloced, assume caller calls free(). */ void dfs(int *nums, int numsSize, int **res, int *returnSize, int **returnColumnSizes, int *path, int pathSize, int *used) { if (pathSize == numsSize) { res[*returnSize] = (int*)malloc(sizeof(int) * pathSize); memcpy(res[*returnSize], path, sizeof(int) * pathSize); (*returnColumnSizes)[*returnSize] = pathSize; (*returnSize)++; return ; } for (int i = 0; i < numsSize; i++) { if (used[i] == 1) { continue; } path[pathSize] = nums[i]; used[i] = 1; dfs(nums, numsSize, res, returnSize, returnColumnSizes, path, pathSize + 1, used); used[i] = 0; } } int** permute(int *nums, int numsSize, int *returnSize, int **returnColumnSizes) { *returnSize = 0; *returnColumnSizes = (int*)malloc(sizeof(int) * 100001); int **res = (int**)malloc(sizeof(int*) * 100001); int *path = (int*)malloc(sizeof(int) * numsSize); int *used = (int*)malloc(sizeof(int) * numsSize); memset(used, 0, numsSize * sizeof(int)); dfs(nums, numsSize, res, returnSize, returnColumnSizes, path, 0, used); return res; }
12. 👌全排列||(不可以重复选取自己,数组中有重复元素)
代码实现:
/** * Return an array of arrays of size *returnSize. * The sizes of the arrays are returned as *returnColumnSizes array. * Note: Both returned array and *columnSizes array must be malloced, assume caller calls free(). */ void dfs(int *nums, int numsSize, int **res, int *returnSize, int **returnColumnSizes, int *path, int pathSize, int *used) { if (pathSize == numsSize) { res[*returnSize] = (int*)malloc(sizeof(int) * pathSize); memcpy(res[*returnSize], path, sizeof(int) * pathSize); (*returnColumnSizes)[*returnSize] = pathSize; (*returnSize)++; return ; } for (int i = 0; i < numsSize; i++) { if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == 0) { // 树层去重 continue; } if (used[i] == 1) { continue; } path[pathSize] = nums[i]; used[i] = 1; dfs(nums, numsSize, res, returnSize, returnColumnSizes, path, pathSize + 1, used); used[i] = 0; } } // 冒泡排序 void bubble_sort(int *arr, int l, int n) { // l:已排好序的元素个数 n:元素总个数 int flag = 1; // 标记位优化 当序列在找到所有的最大值之前就已经将序列排好序了,直接结束循环 for (int i = n - 1; i >= l + 1 && flag; i--) { // i决定了哪一轮冒泡 flag = 0; for (int j = l; j < i; j++) { // j决定哪两个元素进行比较 if (arr[j] > arr[j + 1]) { flag = 1; int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } } int** permuteUnique(int *nums, int numsSize, int *returnSize, int **returnColumnSizes) { bubble_sort(nums, 0, numsSize); // 排序 *returnSize = 0; *returnColumnSizes = (int*)malloc(sizeof(int) * 100001); int **res = (int**)malloc(sizeof(int*) * 100001); int *path = (int*)malloc(sizeof(int) * numsSize); int *used = (int*)malloc(sizeof(int) * numsSize); memset(used, 0, numsSize * sizeof(int)); dfs(nums, numsSize, res, returnSize, returnColumnSizes, path, 0, used); return res; }
13. 👌矩阵中的路径
/************************************************************************
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。
路径可以从矩阵中任意一格开始,每一步可以在矩阵中向左、右、上、下移到一格。如果
一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如在下面的3×4的矩
阵中包含一条字符串“bfce”的路径(路径中的字母用下划线标出)。但矩阵中不包含字符
串“abfb”的路径,因为字符串的第一个字符b占据了矩阵中的第一行第二格之后,路径不能
再次进入这个格子。
A B T G
C F C S
J D E H
************************************************************************/
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#define ROWS 3
#define COLS 4
char map[ROWS][COLS] = {
'A', 'B', 'T', 'G',
'C', 'F', 'C', 'S',
'J', 'D', 'E', 'H'
};
int dir[4][2] = {
{0, 1}, {0, -1}, {1, 0}, {-1, 0}
};
/*
0 1 2 3
4 5 6 7
8 9 10 11
*/
const char *str = "BTCFDE"; // 目标路径
int path[ROWS * COLS]; // 7 6 5 9 存放路径
int pathLength;
bool dfs(int row, int col, int visited[ROWS][COLS]) {
if (map[row][col] == str[pathLength]) {
path[pathLength++] = row * COLS + col;
visited[row][col] = 1;
} else {
return false;
}
// 合法边界
if (pathLength == strlen(str)) {
return true;
}
for (int i = 0; i < 4; i++) {
int x = row + dir[i][0];
int y = col + dir[i][1];
if (x < 0 || x >= ROWS || y < 0 || y >= COLS) {
continue;
}
if (visited[x][y] == 1) {
continue;
}
if (dfs(x, y, visited)) {
return true; // 注意:这一步不能少
}
}
// 回溯
pathLength--;
path[pathLength] = -1; // 删除刚才的结点
visited[row][col] = 0;
return false;
}
bool findPath() {
// 0: 没有走过 1: 走过
int visited[ROWS][COLS]; // 表示各个位置,是否已经走过
pathLength = 0;
memset(visited, 0, sizeof(visited));
memset(path, -1, sizeof(path));
for (int row = 0; row < ROWS; row++) {
for (int col = 0; col < COLS; col++) {
if (dfs(row, col, visited)) {
return true;
}
}
}
return false;
}
int main(int argc, char *argv[]) {
if (findPath()) {
printf("查找成功\n");
for (int i = 0; path[i] >= 0; i++) {
printf("%d ", path[i]);
}
putchar('\n');
} else {
printf("查找失败\n");
}
return 0;
}
14. 重新安排行李
15. 👌N皇后问题
- 不能同行
- 不能同列
- 不能同斜线 (45度和135度角)
代码实现:
bool isValid(char **res, int row, int col, int n) { // Valid:有效的,合理的 // 检查列 for (int i = 0; i < row; i++) { // 这是一个剪枝,不必检查行 if (res[i][col] == 'Q') { return false; } } // 检查 45度角是否有皇后 for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) { if (res[i][j] == 'Q') { return false; } } // 检查 135度角是否有皇后 for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) { if (res[i][j] == 'Q') { return false; } } return true; } // index:行 void dfs(char ***ans, int *returnSize, int **returnColumnSizes, char **res, int index, int n) { // 枚举到最后一行,保存当前棋盘,因为如果不能成为皇后就不能进入下一行 // 所以能到最后一行肯定是有效的 if (index == n) { ans[(*returnSize)] = (char**)malloc(sizeof(char *) * n); for (int i = 0; i < n; i++) { ans[(*returnSize)][i] = (char*)malloc(sizeof(char) * (n + 1)); memcpy(ans[(*returnSize)][i], res[i], sizeof(char) * (n + 1)); } (*returnColumnSizes)[(*returnSize)++] = n; return ; } // 枚举当前行中每一个元素 for (int i = 0; i < n; i++) { // 检查当前位置能不能放皇后 if (isValid(res, index, i, n) == true) { res[index][i] = 'Q'; // 可以直接放皇后 dfs(ans, returnSize, returnColumnSizes, res, index + 1, n); // 进入下一行,重复当前步骤 res[index][i] = '.'; // 回溯,进行判断下一个元素 } } } // returnSize:这是一个指向整数的指针,用于输出 N 皇后问题的解的数目 // returnColumnSizes:这是一个指向整数数组的指针,用于存储每个解的列数 char ***solveNQueens(int n, int *returnSize, int **returnColumnSizes) { char ***ans = (char***)malloc(sizeof(char**) * 1000); *returnColumnSizes = (int*)malloc(sizeof(int) * 1000); *returnSize = 0; char **res = (char**)malloc(sizeof(char *) * n); // 定义棋盘并初始化 for (int i = 0; i < n; i++) { res[i] = (char*)malloc(sizeof(char) * (n + 1)); memset(res[i], '.', sizeof(char) * n); res[i][n] = '\0'; } // 递归枚举每一个位置 dfs(ans, returnSize, returnColumnSizes, res, 0, n); return ans; }
16. 解数独
17. 👌奇怪的电梯
代码实现:
从起点——每一个点的最短距离
#include <stdio.h> #define MAX_N 200 int to[MAX_N + 1]; int dis[MAX_N + 1]; void dfs(int k, int a, int n) { if (dis[a] <= k) { return ; } dis[a] = k; if (a + to[a] <= n) { dfs(k + 1, a + to[a], n); } if (a - to[a] >= 1) { dfs(k + 1, a - to[a], n); } } int main(int argc, char *argv[]) { int n, a, b; scanf("%d%d%d", &n, &a, &b); for (int i = 1; i <= n; i++) { scanf("%d", to + i); } for (int i = 1; i <= n; i++) { dis[i] = n + 1; } dfs(0, a, n); printf("%d\n", dis[b] == n + 1 ? -1 : dis[b]); return 0; }
18. 👌选数(组合)
代码实现:
#include <stdio.h> #include <stdbool.h> #define MAX_N 20 int val[MAX_N + 1]; int ans = 0; bool is_prime(int x) { for (int i = 2; i * i <= x ; i++) { if (x % i == 0) { return false; } } return true; } // ind:选取元素个数 void dfs(int ind, int startindex, int n, int k, int sum) { if (ind == k) { if (is_prime(sum)) { ans += 1; } return ; } for (int i = startindex; i <= n; i++) { dfs(ind + 1, i + 1, n, k, sum + val[i]); } return ; } int main(int argc, char *argv[]) { int n, k; // n个数,选取k个数 scanf("%d%d", &n, &k); for (int i = 1; i <= n; i++) { scanf("%d", val + i); } dfs(0, 1, n, k, 0); printf("%d\n", ans); return 0; }
19. 👌马的遍历
代码实现:
优化1:历史答案剪枝
优化2:编程技巧:方向数组
一个测试样例超时
#include <stdio.h> #define MAX_N 400 int dir[8][2] = { {2, 1}, {-2, 1}, {2, -1}, {-2, -1}, {1, 2}, {1, -2}, {-1, 2}, {-1, -2} }; int dis[MAX_N + 5][MAX_N + 5]; int n, m; // n * m的棋盘 void dfs(int step, int x, int y) { if (dis[x][y] != -1 && dis[x][y] <= step) { return ; } dis[x][y] = step; for (int k = 0; k < 8; k++) { int dx = x + dir[k][0]; int dy = y + dir[k][1]; if (dx < 1 || dx > n) { continue; } if (dy < 1 || dy > m) { continue; } step += 1; dfs(step, dx, dy); // 回溯 step -= 1; } } int main() { int x, y; // 起点坐标 scanf("%d%d%d%d", &n, &m, &x, &y); for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { dis[i][j] = -1; } } dfs(0, x, y); for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (j > 1) { printf(" "); } printf("%d", dis[i][j]); } printf("\n"); } return 0; }
20. 👌自然数拆分 (组合组和||)
代码实现:
#include <stdio.h> int path[10]; int pathSize; void print_one_result(int n) { if (n <= 1) { return ; } for (int i = 0; i < n; i++) { if (i) { putchar('+'); } printf("%d", path[i]); } printf("\n"); } void dfs(int startindex, int n) { if (n == 0) { print_one_result(pathSize); return ; } for (int i = startindex; i <= n; i++) { path[pathSize++] = i; dfs(i, n - i); // 回溯 pathSize--; } } int main(int argc, char *argv[]) { int n; scanf("%d", &n); pathSize = 0; dfs(1, n); return 0; }
21. 👌迷宫
代码实现:
#include <stdio.h> #define MAX_N 5 int dir[4][2] = { {0, 1}, {1, 0}, {0, -1}, {-1, 0} }; int g[MAX_N + 5][MAX_N + 5] = {0}; int n, m, t, x1, x2, y1, y2, ans; void dfs(int x, int y) { if (x == x2 && y == y2) { ans += 1; return ; } g[x][y] = 0; for (int i = 0; i < 4; i++) { int dx = x + dir[i][0]; int dy = y + dir[i][1]; if (g[dx][dy] == 0) { continue; } dfs(dx, dy); } g[x][y] = 1; return ; } int main() { scanf("%d%d%d", &n, &m, &t); for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { g[i][j] = 1; } } scanf("%d%d%d%d", &x1, &y1, &x2, &y2); for (int i = 0, x, y; i < t; i++) { scanf("%d%d", &x, &y); g[x][y] = 0; } ans = 0; dfs(x1, y1); printf("%d\n", ans); return 0; }
22. 👌吃奶酪(排列型枚举问题)
代码实现:超时
#include <stdio.h> #include <math.h> // gcc ... -lm #define MAX_N 15 // 奶酪最大数量 int n; double ans = 1e9; double x[MAX_N + 5], y[MAX_N + 5]; // 奶酪坐标 #define S(a) ((a) * (a)) double __dis(int i, int j) { // 两点之间的距离 return sqrt(S(x[i] - x[j]) + S(y[i] - y[j])); } int used[MAX_N] = {0}; int ind = 0; void dfs(int now, double s, int n) { if (ind == n) { ans = ans < s ? ans : s; return ; } for (int i = 1; i <= n; i++) { if (used[i]) { continue; } ind++; used[i] = 1; dfs(i, s + __dis(now, i), n); // 回溯 ind--; used[i] = 0; } } int main() { scanf("%d", &n); x[0] = y[0] = 0; double a, b; // 输入奶酪坐标 for (int i = 1; i <= n; i++) { scanf("%lf%lf", &a, &b); x[i] = a, y[i] = b; } dfs(0, 0, n); printf("%.2lf\n", ans); return 0; }
23. 单词接龙
代码实现:
#include <stdio.h> #include <string.h> #define MAX_N 20 // 单词最大数量 char s[MAX_N + 5][100]; char start[2]; int n, ans = 0; int vis[MAX_N + 5] = {0}; // 防止用过两个 int d[MAX_N + 5][MAX_N + 5] = {0}; // 记录两两单词共同首尾相同长度 void f() { } void dfs(int x, int L) { vis[x] += 1; if (L > ans) { ans = L; } for (int y = 0; y < n; y++) { if (d[x][y] == 0) { continue; } if (vis[y] == 2) { continue; } dfs(y, L + strlen(s[y]) - d[x][y]); } vis[x] -= 1; return ; } int main() { scanf("%d", &n); // 单词个数 for (int i = 0; i < n; i++) { scanf("%s", s[i]); // 输入每个单词并保存到数组s中 } scanf("%s", start); // 记录两两单词共同首尾相同长度 for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { d[i][j] = f(s[i], s[j]); } } for (int i = 0; i < n; i++) { if (s[i][0] != start[0]) { continue; } dfs(i, strlen(s[i])); } printf("%d\n", ans); return 0; }
24. 👌单词方阵
代码实现:
#include <stdio.h> #include <string.h> #include <stdbool.h> #define MAX_N 100 int n; int dir[8][2] = { {1, 1}, {-1, -1}, {1, 0}, {0, 1}, {1, -1}, {-1, 1}, {-1, 0}, {0, -1} }; char g[MAX_N + 5][MAX_N + 5]; // 单词方阵 int visited[MAX_N + 5][MAX_N + 5]; int path[8]; // 存放路径 int pathSize = 0; int res[MAX_N * MAX_N * MAX_N]; // 记录查找到的目标在哪个位置 char str[8] = "yizhong"; // 目标路径 bool hasPath(int row, int col, int k) { // 边界和字符匹配判断 if (row < 0 || row >= n || col < 0 || col >= n || visited[row][col] || g[row][col] != str[pathSize]) { return false; } path[pathSize++] = row * n + col; visited[row][col] = 1; if (pathSize == strlen(str)) { for (int i = 0; i < 7; i++) { res[path[i]]++; } return true; } // 递归查找路径 int nextRow = row + dir[k][0]; int nextCol = col + dir[k][1]; if (hasPath(nextRow, nextCol, k)) { return true; } // 回溯 pathSize--; visited[row][col] = 0; return false; } int main() { scanf("%d", &n); for (int i = 0; i < n; i++) { // 输入单词方阵 scanf("%s", g[i]); } memset(res, 0, sizeof(res)); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (g[i][j] == 'y') { for (int k = 0; k < 8; k++){ // 以k为方向查找 int x = i + dir[k][0]; int y = j + dir[k][1]; if (hasPath(i, j, k)) { memset(visited, 0, sizeof(visited)); pathSize = 0; } } } } } for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (res[i * n + j] > 0) { printf("%c", g[i][j]); } else { printf("*"); } } printf("\n"); } return 0; }
25. 考前临时抱佛脚
代码实现:
26. 👌回家
代码实现:
#include <stdio.h> #include <stdlib.h> int vis_hp[9][9], vis_step[9][9]; // 记录之前到达该格时小H拥有的最高血量与最小步数 int map[9][9], n, m; // map为地图数组 int min_step = 100; // min_step为最少步数 int dir[4][2] = { // 四个方位 {-1, 0}, {1, 0}, {0, 1}, {0, -1} }; int check(int H_x, int H_y) { if (map[H_x][H_y] == 0 || H_x >= n || H_x < 0 || H_y >= m || H_y < 0) { // 障碍、坐标越界 return 0; // 返回假值 } return 1; // 返回真值 } // H_x、H_y为小H当前坐标,hp为当前生命值,step为当前步数 void DFS(int H_x, int H_y, int hp, int step) { int i; // 可行性剪枝 if (!check(H_x,H_y)) { // 坐标不符要求 return ; } if (hp == 0) { // 生命值为0 return ; } // 最优性剪枝:记忆化 if (hp <= vis_hp[H_x][H_y] && step >= vis_step[H_x][H_y]) { return ; } // 满足所有前提条件后替换两个数组分别记录的最高血量与最小步数 vis_hp[H_x][H_y] = hp; vis_step[H_x][H_y] = step; if (map[H_x][H_y] == 3) { // 生命值大于0且到达家 if (step < min_step) { // 当前步数小于之前找到的最小步数 min_step = step; // 进行替换 } return ; } // 若当前格有鼠标,则补满血量 if (map[H_x][H_y] == 4) { hp = 6; } // 若当前格不是家 for (i = 0; i < 4; i++) { // 分别遍历四个方位; H_x += dir[i][0]; DFS(H_x, H_y + dir[i][1], hp - 1, step + 1); // 回溯 H_x -= dir[i][0]; } } int main() { int x, y; // 输入数据 scanf("%d%d", &n, &m); for (int i = 0; i < n; i++) { for(int j = 0; j < m; j++) { scanf("%d", &map[i][j]); if (map[i][j] == 2) { // 找到起点 x = i; y = j; } } } // 遍历 DFS(x, y, 6, 0); // 输出结果 if (min_step == 100) { printf("-1"); } else { printf("%d\n", min_step); } return 0; }
27. 👌最少操作次数
递归+记忆化
#include <stdio.h> #define MAX_B 100 + 1 int a, b, k; int arr[MAX_B]; // 记忆化 int result = MAX_B; void dfs(int step, int a) { if (a > b) { return ; } if (arr[a] < step) { return ; } arr[a] = step; if (a == b) { result = result > step ? step : result; return ; } dfs(step + 1, a * k); dfs(step + 1, a + 1); } int main() { scanf("%d%d%d", &a, &b, &k); for (int i = 0; i < MAX_B; i++) { arr[i] = MAX_B; } dfs(0, a); printf("%d\n", result); return 0; }
28. 👌划分字母区间
分割字符串:
29. 字串变换
优化:
迭代加深---->控制递归深度
#include <stdio.h> #include <string.h> #include <stdlib.h> #define MAX_NUM 11 #define MAX_TRANS 6 int rule_num = 0; // 规则数量 int ans_k; // 递归深度 int ans = MAX_NUM; // 变换次数 char a[21], b[21]; // 字串A, 字串B char from[MAX_TRANS][21], to[MAX_TRANS][21]; // 规则 void dfs(char *now, int step) { if (step > ans_k || ans <= step) { // 当前步数大于递归深度 return ; } if (strcmp(now, b) == 0) { ans = ans > step ? step : ans; return ; } int ind = 0; for (int i = 0; i < rule_num; i++) { int flag = 0; for (int k = 0; now[k]; k++) { // 找到匹配的下标 flag = 1; for (int j = 0; from[i][j]; j++) { if (now[k + j] == from[i][j]) { continue; } flag = 0; break; } if (flag == 1) { ind = k; break; } } while (flag) { // 匹配成功 char path[1000] = "\0"; strncpy(path, now, ind); strcat(path, to[i]); strcat(path, now + ind + strlen(from[i])); dfs(path, step + 1); // 接着往下找 flag = 0; for (int k = ind + 1; now[k]; k++) { // 找到匹配的下标 flag = 1; for (int j = 0; from[i][j]; j++) { if (now[k + j] == from[i][j]) { continue; } flag = 0; break; } if (flag == 1) { ind = k; break; } } } } } int main() { scanf("%s %s", a, b); while (scanf("%s %s", from[rule_num], to[rule_num]) != EOF) { rule_num++; // 规则数量 } for (int i = 1; i <= 10; i++) { ans_k = i; // 递归深度 dfs(a, 0); if (ans != MAX_NUM) { break; } } if (ans == MAX_NUM) { printf("NO ANSWER!\n"); } else { printf("%d\n", ans); } return 0; }
使用strstr函数
#include <stdio.h> #include <string.h> #include <stdlib.h> #define MAX_NUM 11 #define MAX_TRANS 6 int rule_num = 0; // 规则数量 int ans_k; // 递归深度 int ans = MAX_NUM; // 变换次数 char a[21], b[21]; // 字串A, 字串B char from[MAX_TRANS][21], to[MAX_TRANS][21]; // 规则 void dfs(char *now, int step) { if (step > ans_k || ans <= step) { // 当前步数大于递归深度 || ans <= step return ; } if (strcmp(now, b) == 0) { ans = ans > step ? step : ans; return ; } for (int i = 0; i < rule_num; i++) { char *pos = strstr(now, from[i]); while (pos != NULL) { char path[1000] = "\0"; strncpy(path, now, pos - now); strcat(path, to[i]); strcat(path, now + (pos - now) + strlen(from[i])); dfs(path, step + 1); pos = strstr(now + (pos - now) + 1, from[i]); } } return ; } int main() { scanf("%s %s", a, b); while (scanf("%s %s", from[rule_num], to[rule_num]) != EOF) { rule_num++; // 规则数量 } for (int i = 1; i <= 10; i++) { ans_k = i; // 递归深度 dfs(a, 0); if (ans != MAX_NUM) { break; } } if (ans == MAX_NUM) { printf("NO ANSWER!\n"); } else { printf("%d\n", ans); } return 0; }
30. 移动字母
代码实现:
方法一:bfs + 队列
#include <stdio.h> #include <string.h> char s[7]; // 用来保存初始状态字符串 char st[10000][7]; // 保存所有可能的状态字符串 int st_size = 0; // 保存状态字符串的数量 int dirx[4] = {1, -1, 0, 0}; // 定义x方向的四个方向 int diry[4] = {0, 0, 1, -1}; // 定义y方向的四个方向 // 定义广度优先搜索函数 void bfs() { strcpy(st[st_size], s); st_size++; int front = 0, rear = 0; while (front <= rear) { char p[7]; strcpy(p, st[front]); front++; // 找到'*'的坐标 int idx = 0; for (int i = 0; i < 6; i++) { if (p[i] == '*') { idx = i; break; } } int x = idx / 3; int y = idx % 3; // 遍历四个方向 for (int i = 0; i < 4; i++) { int dx = x + dirx[i]; int dy = y + diry[i]; // 判断是否在范围内 if (dx >= 0 && dx < 2 && dy >= 0 && dy < 3) { char temp = p[idx]; p[idx] = p[dx * 3 + dy]; p[dx * 3 + dy] = temp; int found = 0; for (int j = 0; j < st_size; j++) { if (strcmp(p, st[j]) == 0) { found = 1; break; } } // 如果新状态不在st中,则加入队列 if (!found) { strcpy(st[st_size], p); st_size++; rear++; } // 回溯 temp = p[idx]; p[idx] = p[dx * 3 + dy]; p[dx * 3 + dy] = temp; } } } } int main() { strcpy(s, "ABCDE*"); int n; scanf("%d", &n); char vs[n][7]; for (int i = 0; i < n; i++) { scanf("%s", vs[i]); } bfs(); for (int i = 0; i < n; i++) { int found = 0; for (int j = 0; j < st_size; j++) { if (strcmp(vs[i], st[j]) == 0) { found = 1; break; } } if (found) { printf("1\n"); } else { printf("0\n"); } } return 0; }
方法二:dfs
31. 机器人搬重物
代码实现:
方法一:bfs + 队列
#include <stdio.h> #include <stdbool.h> bool a[55][55]; // 原始数据,存下不能去的点 bool aa[55][55][4]; // 定义一个bool空间存储那些位置+方向访问过 // 队列,b[i][0]横坐标,b[i][1]纵坐标,b[i][2]朝向,b[i][3]第几步 int b[10000][4]; int n, m, x0, y0, x1, y1; int step = 1, flag = 0; char c; int front = 0, rear = 1; // 左转右转操作 int turn(int u, bool lr) { if (lr == 0) { // 左转 if (u == 0) return 3; else return u - 1; } else { // 右转 if (u == 3) return 0; else return u + 1; } } void bfs() { int i, x, y, d, step; while (front < rear) { x = b[front][0]; y = b[front][1]; d = b[front][2]; step = b[front][3]; if (x == x1 && y == y1) { printf("%d\n", b[front][3]); return; } if (aa[x][y][turn(d, 0)] == 0) { // 左转 aa[x][y][turn(d, 0)] = 1; b[rear][0] = x; b[rear][1] = y; b[rear][2] = turn(d, 0); b[rear++][3] = step + 1; } if (aa[x][y][turn(d, 1)] == 0) { // 右转 aa[x][y][turn(d, 1)] = 1; b[rear][0] = x; b[rear][1] = y; b[rear][2] = turn(d, 1); b[rear++][3] = step + 1; } for (i = 1; i <= 3; i++) { if (d == 0 && x - i > 0 && aa[x - i][y][d] == 0 && a[x - 1][y] == 0 && a[x - i][y] == 0) { aa[x - i][y][d] = 1; b[rear][0] = x - i; b[rear][1] = y; b[rear][2] = d; b[rear++][3] = step + 1; } if (d == 2 && x + i < n && aa[x + i][y][d] == 0 && a[x + 1][y] == 0 && a[x + i][y] == 0) { aa[x + i][y][d] = 1; b[rear][0] = x + i; b[rear][1] = y; b[rear][2] = d; b[rear++][3] = step + 1; } if (d == 3 && y - i > 0 && aa[x][y - i][d] == 0 && a[x][y - 1] == 0 && a[x][y - i] == 0) { aa[x][y - i][d] = 1; b[rear][0] = x; b[rear][1] = y - i; b[rear][2] = d; b[rear++][3] = step + 1; } if (d == 1 && y + i < m && aa[x][y + i][d] == 0 && a[x][y + 1] == 0 && a[x][y + i] == 0) { aa[x][y + i][d] = 1; b[rear][0] = x; b[rear][1] = y + i; b[rear][2] = d; b[rear++][3] = step + 1; } } front++; } printf("-1\n"); } int main() { int d, t; scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { scanf("%d", &t); if (t == 1) { a[i][j] = a[i - 1][j] = a[i][j - 1] = a[i - 1][j - 1] = 1; } } } scanf("%d%d%d%d", &x0, &y0, &x1, &y1); getchar(); // 吃掉空格 scanf("%c", &c); if (c == 'N') d = 0; // 北 else if (c == 'E') d = 1; // 东 else if (c == 'S') d = 2; // 南 else d = 3; // 西 aa[x0][y0][d] = 1; b[0][0] = x0; b[0][1] = y0; b[0][2] = d; b[0][3] = 0; bfs(); return 0; }
方法二:dfs
32. 组合总和 IV
代码实现:
方法一:回溯——超时
#define swap(a, b) { \ __typeof(a) __c = a; \ a = b, b = __c; \ } int num; // 快排 void sort(int *nums, int l, int r) { if (r - l <= 2) { if (r - l <= 1) { return; } if (nums[r - 1] > nums[l]) { swap(nums[r - 1], nums[l]); } } int i = l, j = r - 1; int a = nums[i]; while (i < j) { while (i < j && nums[j] >= a) { j--; } if (i < j) { nums[i++] = nums[j]; } while (i < j && nums[i] <= a) { i++; } if (i < j) { nums[j--] = nums[i]; } } nums[i] = a; sort(nums, l, i); sort(nums, i + 1, r); } void dfs(int *nums, int numsSize, int target, int sum) { if (sum > target) { return; } if (sum == target) { num++; return; } for (int i = 0; i < numsSize; i++) { dfs(nums, numsSize, target, sum + nums[i]); } } int combinationSum4(int *nums, int numsSize, int target) { num = 0; sort(nums, 0, numsSize); dfs(nums, numsSize, target, 0); return num; }
方法二:动态规划——背包问题
33. 连续差相同的数字
代码实现:
void dfs(int n, int k, int start, int *res, int* returnSize, int sum, int idx) { if (start < 0 || start > 9) { return; } // 对满足条件的值进行累和 sum = sum * 10 + start; // 长度满足条件, 数值输出 if (idx == n - 1) { res[(*returnSize)++] = sum; return; } // 数据分别加k, 减k if (k != 0) { dfs(n, k, start + k, res, returnSize, sum, idx + 1); } dfs(n, k, start - k, res, returnSize, sum, idx + 1); } int* numsSameConsecDiff(int n, int k, int *returnSize) { int *res = (int*)malloc(sizeof(int) * 10000); *returnSize = 0; // 起始从1-9开始 for (int i = 1; i < 10; i++) { dfs(n, k, i, res, returnSize, 0, 0); } return res; }
34. 单词转换
代码实现:
/** * Note: The returned array must be malloced, assume caller calls free(). */ // 判断原字符串与目标字符串是否只相差一个字符 bool isOneDiff(char *curWord, char *cmpWord) { int len = strlen(curWord); int sum = 0; for (int i = 0; i < len; i++) { if (curWord[i] != cmpWord[i]) { sum++; } } if (sum == 1) { return true; } return false; } // 深度优先搜索 bool DFS(char *curWord, char *tarWord, char **retArr, int *arrLen, char **wordList, int wordListSize, int *avail) { // 如果与目标字符串相同了,说明找到了,返回true if (strcmp(curWord, tarWord) == 0) { return true; } // 为当前层次的字符串申请空间 retArr[(*arrLen)] = (char*)malloc(sizeof(char) * (strlen(curWord) + 1)); for (int i = 0; i < wordListSize; i++) { // 如果遍历到的字符串被遍历过或者不是相差一个字符就跳过 if (avail[i] == -1 || !isOneDiff(curWord, wordList[i])) { continue; } // 如果只相差一个字符并且没有被遍历过就记录下来 strcpy(retArr[(*arrLen)], wordList[i]); (*arrLen)++; // 并标记对应的位置 avail[i] = -1; // 继续深度优先搜索 bool isRight = DFS(wordList[i], tarWord, retArr, arrLen, wordList, wordListSize, avail); if (isRight) { // 如果能找到对应的字符串就返回true return true; } // 否则就不记录该字符串,但是注意,字符串本身依旧是被标记过的 (*arrLen)--; } // 都不满足就返回false return false; } char** findLadders(char *beginWord, char *endWord, char **wordList, int wordListSize, int *returnSize) { // 用于标记的数组 int *avail = (int *)malloc(sizeof(int) * wordListSize); for (int i = 0; i < wordListSize; i++) { avail[i] = i; } // 记录初始字符串 char **retArr = (char**)malloc(sizeof(char*) * (wordListSize + 2)); int retLen = 0; retArr[retLen] = (char*)malloc(sizeof(char) * (strlen(beginWord) + 1)); strcpy(retArr[retLen], beginWord); retLen++; retArr[retLen] = (char *)malloc(sizeof(char) * (strlen(beginWord) + 1)); // 开始遍历 for (int i = 0; i < wordListSize; i++) { if (isOneDiff(beginWord, wordList[i])) { strcpy(retArr[retLen], wordList[i]); avail[i] = -1; retLen++; bool isRight = DFS(wordList[i], endWord, retArr, &retLen, wordList, wordListSize, avail); if (isRight) { *returnSize = retLen; return retArr; } retLen--; } } // 如果不满足条件就返回空 *returnSize = 0; return NULL; }