代码随想录算法训练营第32天-51. N皇后

另外两道之后再写

51. N皇后

N皇后问题,算是dfs最经典的问题了,数据结构课一定会碰到的问题。

简单来说就是N*N的矩阵中,在任一行,列,对角线,都只允许出现一个皇后。

尽管在力扣上是困难题,其实这个题就是判断条件比较复杂

1.判断条件

n皇后的判断条件就是四个:

  • 行唯一
  • 列唯一
  • 对角线唯一
  • 反对角线唯一

我们可以为所有行、列、对角线、反对角线设置一个bool数组,用来记录是否当前行、列、对角线、反对角线已经有皇后

额外一提,当棋盘为 n,行列数为 n,对角线数为 2*n - 1,根据行列可以计算所在的对角线数

根据行列计算对角线数这个建议自己去琢磨,把这个攻破了这题其实和一般的回溯没什么区别

2.自己写的

vector<vector<string>> res;
vector<string> path;

vector<bool> row;
vector<bool> col;
vector<bool> diag;
vector<bool> back_diag;

void backtracing(int n, int cur_row)
{
    if (path.size() == n)
    {
        res.push_back(path);
        return;
    }

    string str(n, '.');

    for (int cur_col = 0; cur_col < n; cur_col++)
    {
        if (col[cur_col] == false && diag[n - 1 - cur_col + cur_row] == false && back_diag[cur_row + cur_col] == false)
        {
            row[cur_row] = true;
            col[cur_col] = true;
            diag[n - 1 - cur_col + cur_row] = true;
            back_diag[cur_row + cur_col] = true;

            str[cur_col] = 'Q';
            path.push_back(str);

            backtracing(n, cur_row + 1);

            path.pop_back();
            str[cur_col] = '.';

            row[cur_row] = false;
            col[cur_col] = false;
            diag[n - 1 - cur_col + cur_row] = false;
            back_diag[cur_row + cur_col] = false;
        }
    }
}

vector<vector<string>> solveNQueens(int n) {
    res.clear();
    path.clear();

    for (int i = 0; i < n; i++)
    {
        row.push_back(false);
        col.push_back(false);
    }

    for (int i = 0; i < 2 * n - 1; i++)
    {
        diag.push_back(false);
        back_diag.push_back(false);
    }

    backtracing(n, 0);

    return res;
}

3.代码随想录的方法

代码随想录是把这个判别写了一个函数单独拎出来,然后用遍历的方式去检查,和我的思路还是不太一样,所以不妨写写看

这个方法中不再有那些布尔数组,而是直接利用模拟棋盘判断

注意,这个模式下不用检查同行,因为同行中只会出现一个

vector<vector<string>> res;
vector<string> chessboard;  //棋盘

void backtracing(int n, int cur_row)
{
    if (cur_row == n)
    {
        res.push_back(chessboard);
        return;
    }

    for (int cur_col = 0; cur_col < n; cur_col++)
    {
        if (isValid(cur_row, cur_col, n))
        {
            chessboard[cur_row][cur_col] = 'Q';
            backtracing(n, cur_row + 1);
            chessboard[cur_row][cur_col] = '.';
        }
    }
}

bool isValid(int row, int col, int n)
{
    int i, j;
    //检查同列
    for (i = 0, j = col; i < n; i++)
        if (chessboard[i][j] == 'Q') 
            return false;

    //检查正对角线
    for (i = row, j = col; i > 0 && j > 0; i--, j--);
    for (;i < n && j < n; i++, j++)
        if (chessboard[i][j] == 'Q')
            return false;

    //检查反对角线
    for (i = row, j = col; i > 0 && j < n; i--, j++);
    for (;i < n && j > 0; i++, j--)
        if (chessboard[i][j] == 'Q')
            return false;

    return true;
}

vector<vector<string>> solveNQueens(int n) {
    res.clear();
    chessboard.clear();

    for (int i = 0; i < n; i++)
        chessboard.push_back(string(n, '.'));      

    backtracing(n, 0);

    return res;
}

4.比较怪的写法

我在学数据结构的时候,老师讲过可以用每一个格子每一个格子的方式深度优先,每个格子要么放,要么不放。但是这个树看起来估计会很大(深度n*n)

速度确实慢中慢哈哈哈

这个记得是要检查同行的,而且需要记录放的皇后数量,来确认是否为目标结果

vector<vector<string>> res;
vector<string> chessboard;  //棋盘

void backtracing(int n, int cur_row, int cur_col, int cnt)
{
    if (cur_row >= n)
    { 
        if (cnt == n) res.push_back(chessboard);
        return;
    }

    int next_row = cur_row;
    int next_col = cur_col;

    if (cur_col < n - 1)
        next_col++;
    else
    {
        next_row++;
        next_col = 0;
    }

    backtracing(n, next_row, next_col, cnt);

    if (isValid(cur_row, cur_col, n))
    {
        chessboard[cur_row][cur_col] = 'Q';
        backtracing(n, next_row, next_col, cnt + 1);
        chessboard[cur_row][cur_col] = '.';
    }
}

bool isValid(int row, int col, int n)
    {
        int i, j;
        //检查同列
        for (i = 0, j = col; i < n; i++)
            if (chessboard[i][j] == 'Q') 
                return false;

        //检查同行
        for (i = row, j = 0; j < n; j++)
            if (chessboard[i][j] == 'Q') 
                return false;

        //检查正对角线
        for (i = row, j = col; i > 0 && j > 0; i--, j--);
        for (;i < n && j < n; i++, j++)
            if (chessboard[i][j] == 'Q')
                return false;

        //检查反对角线
        for (i = row, j = col; i > 0 && j < n; i--, j++);
        for (;i < n && j > 0; i++, j--)
            if (chessboard[i][j] == 'Q')
                return false;

        return true;
    }
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
第二十二算法训练营主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组中找到长度最小的子数组,使得子数组的和大于等于给定的目标值。这里可以使用滑动窗口的方法来解决问题。使用两个指针来表示滑动窗口的左边界和右边界,通过移动指针来调整滑动窗口的大小,使得滑动窗口中的元素的和满足题目要求。具体实现的代码如下: ```python def minSubArrayLen(self, target: int, nums: List[int]) -> int: left = 0 right = 0 ans = float('inf') total = 0 while right < len(nums): total += nums[right] while total >= target: ans = min(ans, right - left + 1) total -= nums[left] left += 1 right += 1 return ans if ans != float('inf') else 0 ``` 以上就是第二十二算法训练营的内容。通过这些题目的练习,可以提升对双指针和滑动窗口等算法的理解和应用能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

去人777

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值