代码随想录第二十六天(一刷&&C语言)|N皇后&&解数独

创作目的:为了方便自己后续复习重点,以及养成写博客的习惯。

回溯模板如下:

void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }
    for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
        处理节点;
        backtracking(路径,选择列表); // 递归
        回溯,撤销处理结果
    }
}

一、N皇后

思路:参考carl文档

        二维矩阵中矩阵的高就是这棵树的高度,矩阵的宽就是树形结构中每一个节点的宽度。二维数组result来记录最终结果。参数n是棋盘的大小,用row来记录当前遍历到棋盘的第几层了。递归深度就是row控制棋盘的行,每一层里for循环的col控制棋盘的列,由此确定皇后的位置。

ledcode题目:https://leetcode.cn/problems/n-queens/description/

AC代码:

char ***ans;
char **path;
int ansTop, pathTop;
//将path中元素复制到ans中
void copyPath(int n) {
    char **tempPath = (char**)malloc(sizeof(char*) * pathTop);
    int i;
    for(i = 0; i < pathTop; ++i) {
        tempPath[i] = (char*)malloc(sizeof(char) * n + 1);
        int j;
        for(j = 0; j < n; ++j)
            tempPath[i][j] = path[i][j];
        tempPath[i][j] = '\0';

    }
    ans[ansTop++] = tempPath;
}

//判断当前位置是否有效(是否不被其它皇后影响)
int isValid(int x, int y, int n) {
    int i, j;
    //检查同一行以及同一列是否有效
    for(i = 0; i < n; ++i) {
        if(path[y][i] == 'Q' || path[i][x] == 'Q')
            return 0;
    }
    //下面两个for循环检查斜角45度是否有效
    i = y - 1;
    j = x - 1;
    while(i >= 0 && j >= 0) {
        if(path[i][j] == 'Q')
            return 0;
        --i, --j;
    }

    i = y + 1;
    j = x + 1;
    while(i < n && j < n) {
        if(path[i][j] == 'Q')
            return 0;
        ++i, ++j;
    }

    //下面两个for循环检查135度是否有效
    i = y - 1;
    j = x + 1;
    while(i >= 0 && j < n) {
        if(path[i][j] == 'Q')
            return 0;
        --i, ++j;
    }

    i = y + 1;
    j = x -1;
    while(j >= 0 && i < n) {
        if(path[i][j] == 'Q')
            return 0;
        ++i, --j;
    }
    return 1;
}

void backTracking(int n, int depth) {
    //若path中有四个元素,将其拷贝到ans中。从当前层返回
    if(pathTop == n) {
        copyPath(n);
        return;
    }

    //遍历横向棋盘
    int i;
    for(i = 0; i < n; ++i) {
        //若当前位置有效
        if(isValid(i, depth, n)) {
            //在当前位置放置皇后
            path[depth][i] = 'Q';
            //path中元素数量+1
            ++pathTop;

            backTracking(n, depth + 1);
            //进行回溯
            path[depth][i] = '.';
            //path中元素数量-1
            --pathTop;
        }
    }
}

//初始化存储char*数组path,将path中所有元素设为'.'
void initPath(int n) {
    int i, j;
    for(i = 0; i < n; i++) {
        //为path中每个char*开辟空间
        path[i] = (char*)malloc(sizeof(char) * n + 1);
        //将path中所有字符设为'.'
        for(j = 0; j < n; j++)
            path[i][j] = '.';
        //在每个字符串结尾加入'\0'
        path[i][j] = '\0';
    }
}

char *** solveNQueens(int n, int* returnSize, int** returnColumnSizes){
    //初始化辅助变量
    ans = (char***)malloc(sizeof(char**) * 400);
    path = (char**)malloc(sizeof(char*) * n);
    ansTop = pathTop = 0;

    //初始化path数组
    initPath(n);
    backTracking(n, 0);

    //设置返回数组大小
    *returnSize = ansTop;
    int i; 
    *returnColumnSizes = (int*)malloc(sizeof(int) * ansTop);
    for(i = 0; i < ansTop; ++i) {
        (*returnColumnSizes)[i] = n;
    }
    return ans;
}

二、解数独

思路:参考carl文档

        使用bool变量在解数独时找到一个符合的条件(就在树的叶子节点上)立刻就返回,相当于找从根节点到叶子节点一条唯一路径。一个for循环遍历棋盘的行,一个for循环遍历棋盘的列,一行一列确定下来之后,递归遍历这个位置放9个数字的可能性。注意判断棋盘是否合法。

lecode题目:https://leetcode.cn/problems/sudoku-solver/

AC代码:

bool isValid(char** board, int row, int col, int k) {
    /* 判断当前行是否有重复元素 */
    for (int i = 0; i < 9; i++) {
        if (board[i][col] == k) {
            return false;
        }
    }
    /* 判断当前列是否有重复元素 */
    for (int j = 0; j < 9; j++) {
        if (board[row][j] == k) {
            return false;
        }
    }
    /* 计算当前9宫格左上角的位置 */
    int startRow = (row / 3) * 3;
    int startCol = (col / 3) * 3;
    /* 判断当前元素所在九宫格是否有重复元素 */
    for (int i = startRow; i < startRow + 3; i++) {
        for (int j = startCol; j < startCol + 3; j++) {
            if (board[i][j] == k) {
                return false;
            }
        }
    }
    /* 满足条件,返回true */
    return true;
}

bool backtracking(char** board, int boardSize, int* boardColSize) {
    /* 从上到下、从左到右依次遍历输入数组 */
    for (int i = 0; i < boardSize; i++) {
        for (int j = 0; j < *boardColSize; j++) {
            /* 遇到数字跳过 */
            if (board[i][j] != '.') {
                continue;
            }
            /* 依次将数组1到9填入当前位置 */
            for (int k = '1'; k <= '9'; k++) {
                /* 判断当前位置是否与满足条件,是则进入下一层 */
                if (isValid(board, i, j, k)) {
                    board[i][j] = k;
                    /* 判断下一层递归之后是否找到一种解法,是则返回true */
                    if (backtracking(board, boardSize, boardColSize)) {
                        return true;
                    }
                    /* 回溯,将当前位置清零 */
                    board[i][j] = '.';
                }
            }
            /* 若填入的9个数均不满足条件,返回false,说明此解法无效 */
            return false;
        }
    }
    /* 遍历完所有的棋盘,没有返回false,说明找到了解法,返回true */
    return true;
}

void solveSudoku(char** board, int boardSize, int* boardColSize) {
    bool res = backtracking(board, boardSize, boardColSize);
}

全篇后记:

        思路清晰许多,但似乎没有遇到实际的需求,也没有花费很多时间来进行思考。目前只能安慰自己一刷不要太在意了,来日方长,或许坚持下去会得到不一样的结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值