算法——回溯

一:回溯法理论基础

递归进化,请叫【回溯】算法!

        递归:关注代码实现

        回溯:关注问题解决

「回溯是递归的副产品,只要有递归就会有回溯」

回溯法也可以叫做回溯搜索法,它是一种搜索的方式

回溯法就是暴力搜索,并不是什么高效的算法,最多再剪枝一下

回溯法------> 深度优先搜索(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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值