力扣51题(n皇后问题、递归、回溯)

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/n-queens
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

51.n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。

  1. 对于国际象棋我也是一窍不同,但是n皇后问题就是研究在n*n棋盘如何放置皇后,使得任何两个皇后都不能处于同一条横行、纵行或斜线上。
  2. 本来看到题目的时候还是比较懵的,因为不知道要以何种形式表示每一种摆法以及要如何去获得每一种摆法。但是看了题解后有点豁然开朗,用一个n*n数组来代表棋盘上每一个格子,然后初始化每个元素都为‘.’,然后定义一个isValid的函数来判断如果皇后落在某个位置是否合法。于是开始进入递归函数,每一层递归相当于在每一行中找到一个合适的位置放置皇后,然后当每一行都放置完皇后之后,我们就找到一个合适的解法了。了解了这个思路之后来看代码就很清晰了。
/**
 * 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().
 */

//用于检查在chessboard[row][col]位置放置皇后是否合法
bool isValid(int row, int col, char** chessboard, int n) { 
    //检查同一列上是否已经有皇后
    for (int i = 0; i < row; i++) { 
        if (chessboard[i][col] == 'Q')
            return false;
    }

    // 由于在每次for循环只会选取一个位置放置皇后Q,所以不用检查同一行上是否已经有皇后

    //检查左斜线上是否有皇后
    for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
        if (chessboard[i][j] == 'Q')
            return false;
    }

    //检查右斜线上是否有皇后
    for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
        if (chessboard[i][j] == 'Q')
            return false;
    }
    return true;
}

// row用来记录棋盘上已经有多少行放置了皇后
void backtracking(int n, char*** res, int* returnSize, char** chessboard, int row) {
    if (row == n) { //当row == n 则代表chessboard上已经有一种解法
        res[*returnSize] = malloc(sizeof(char*) * n);
        for (int i = 0; i < n; i++) {
            res[*returnSize][i] = malloc(sizeof(char) * n);
            for (int j = 0; j < n; j++) {
                res[*returnSize][i][j] = chessboard[i][j];
            }
        }
        (*returnSize)++;
        return;
    }
    for (int col = 0; col < n; col++) {
        //验证合法就可以在chessboard[row][col]位置上放置皇后
        if (isValid(row, col, chessboard, n)) {
            chessboard[row][col] = 'Q';
            backtracking(n, res, returnSize, chessboard, row + 1);
            chessboard[row][col] = '.'; //回溯,撤销放置皇后
        }
    }
}

char *** solveNQueens(int n, int* returnSize, int** returnColumnSizes){
    *returnSize = 0;
    char*** res = malloc(sizeof(char**) * 1000);
    //新建一个n*n数组代表棋盘,初始化每个元素为.
    char** chessboard = malloc(sizeof(char*) * n); 
    for (int i = 0; i < n; i++) {
        chessboard[i] = malloc(sizeof(char) * n);
        for (int j = 0; j < n; j++) {
            chessboard[i][j] = '.';
        }
    }   
    backtracking(n, res, returnSize, chessboard, 0);
    *returnColumnSizes = malloc(sizeof(int) * (*returnSize));
    for (int i = 0; i < *returnSize; i++) {
        (*returnColumnSizes)[i] = n;
    }
    return res;
}
  1. 明明感觉逻辑没有错,但是代码一直过不了,总是报错AddressSanitizer。
  2. 经过苦苦几个小时的找错,终于发现这个天坑,居然是结果数组中每个字符串没有加结束符’\0’惹的祸!真的是太坑了!
/**
 * 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().
 */

//用于检查在chessboard[row][col]位置放置皇后是否合法
bool isValid(int row, int col, char** chessboard, int n) { 
    //检查同一列上是否已经有皇后
    for (int i = 0; i < row; i++) { 
        if (chessboard[i][col] == 'Q')
            return false;
    }

    // 由于在每次for循环只会选取一个位置放置皇后Q,所以不用检查同一行上是否已经有皇后

    //检查左斜线上是否有皇后
    for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
        if (chessboard[i][j] == 'Q')
            return false;
    }

    //检查右斜线上是否有皇后
    for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
        if (chessboard[i][j] == 'Q')
            return false;
    }
    return true;
}

// row用来记录棋盘上已经有多少行放置了皇后
void backtracking(int n, char*** res, int* returnSize, char** chessboard, int row) {
    if (row == n) { //当row == n 则代表chessboard上已经有一种解法
        res[*returnSize] = malloc(sizeof(char*) * n);
        for (int i = 0; i < n; i++) {
            res[*returnSize][i] = malloc(sizeof(char) * (n + 1));
            int j = 0;
            for (j = 0; j < n; j++) {
                res[*returnSize][i][j] = chessboard[i][j];
            }
            res[*returnSize][i][j] = '\0'; //巨坑!!!!!
        }
        (*returnSize)++;
        return;
    }
    for (int col = 0; col < n; col++) {
        //验证合法就可以在chessboard[row][col]位置上放置皇后
        if (isValid(row, col, chessboard, n)) {
            chessboard[row][col] = 'Q';
            backtracking(n, res, returnSize, chessboard, row + 1);
            chessboard[row][col] = '.'; //回溯,撤销放置皇后
        }
    }
}

char *** solveNQueens(int n, int* returnSize, int** returnColumnSizes){
    *returnSize = 0;
    char*** res = malloc(sizeof(char**) * 1000);
    //新建一个n*n数组代表棋盘,初始化每个元素为.
    char** chessboard = malloc(sizeof(char*) * n); 
    for (int i = 0; i < n; i++) {
        chessboard[i] = malloc(sizeof(char) * n);
        for (int j = 0; j < n; j++) {
            chessboard[i][j] = '.';
        }
    }   
    backtracking(n, res, returnSize, chessboard, 0);
    *returnColumnSizes = malloc(sizeof(int) * (*returnSize));
    for (int i = 0; i < *returnSize; i++) {
        (*returnColumnSizes)[i] = n;
    }
    return res;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值