【递归、搜索与回溯】综合练习(三)

📝前言说明:

  • 本专栏主要记录本人递归,搜索与回溯算法的学习以及LeetCode刷题记录,按专题划分
  • 每题主要记录:(1)本人解法 + 本人屎山代码;(2)优质解法 + 优质代码;(3)精益求精,更好的解法和独特的思想(如果有的话)
  • 文章中的理解仅为个人理解。如有错误,感谢纠错

🎬个人简介:努力学习ing
📋本专栏:C++刷题专栏
📋其他专栏:C语言入门基础python入门基础C++学习笔记Linux
🎀CSDN主页 愚润泽

你可以点击下方链接,进行该专题内不同子专题的学习

点击链接开始学习
导论递归 (一)递归 (二)
二叉树的深搜穷举 vs 暴搜 vs 深搜 vs 回溯 vs 剪枝
综合练习(一)综合练习(二)
综合练习(三)综合练习(四)
FloodFill(一)FloodFill(二)
记忆化搜索(一)记忆化搜索(二)

题单汇总链接:点击 → 题单汇总


526. 优美的排列

题目链接:https://leetcode.cn/problems/beautiful-arrangement/description/
在这里插入图片描述

个人解

思路:

  • 题目不难,题目有点歧义
  • 首先,perm数组是1 - n所有数字都要有
  • 其次,perm中的元素只要满足条件之一就属于“能选”

用时:10:00
屎山代码:

class Solution {
public:
    int ans;
    bool check[16];

    void dfs(int n, int pos) // pos 是下标
    {
        if(pos == n + 1) // 处理完 n 个数字
        {
            ans++;
            return;
        }
        for(int i = 1; i <= n; i++) // perm 每个位置的取值范围是 [1 - n]
        {
            if(!check[i] && (pos % i == 0|| i % pos == 0))
            {
                check[i] = true;
                dfs(n, pos + 1); // 填下一个位置
                check[i] = false;
            }
        }
    }
    int countArrangement(int n) {
        dfs(n, 1); // 下标从 1 开始
        return ans;
    }
};

时间复杂度: O ( n ! ) O(n!) O(n!),递归深度为n,每个位置有 i 个选择,i 属于[n , 1]
空间复杂度: O ( n ) O(n) O(n)


51. N 皇后

题目链接:https://leetcode.cn/problems/n-queens/description/
在这里插入图片描述

个人解

思路:

  • 决策树的每一层处理的是一行的选择(解决了同行),还要解决同列 和 对角线
  • 要用行号和列号描述出对应的对角线

用时:35:00
屎山代码:

class Solution {
public:
    vector<vector<string>> ans;
    vector<string> board;
    string line;
    vector<bool> col, diag1, diag2;

    void dfs(int n, int pos) // pos 代表当前的行号
    {
        if(board.size() == n)
        {
            ans.emplace_back(board);
            return;
        }
        for(int i = 0; i < n; i++) // 遍历所有列, 找当前行的适当位置
        {
            int d1 = pos - i + n - 1; // 这个元素对应的主对角线的下标,同时防止越界
            int d2 = pos + i;
            if(col[i] == false && diag1[d1] == false && diag2[d2] == false) // 代表这个位置可选
            {
                string tmp = line;
                tmp[i] = 'Q';
                board.emplace_back(tmp);
                col[i] = true; diag1[d1] = true; diag2[d2] = true;
                dfs(n, pos + 1); // 下一行
                board.pop_back();
                col[i] = false; diag1[d1] = false;  diag2[d2] = false; // 回溯
            }
        }
    }
    vector<vector<string>> solveNQueens(int n) {
        // 一行一行选,同时确保皇后在不同列以及不同的对角线上
        // 对于对角线: 主对角线: 行号 - 列号是相同的数字, 副对角线: 行号 + 列号是相同的数字
        line = string(n, '.');
        col = vector<bool>(n, false);
        diag1 = vector<bool>(2 * n - 1, false);
        diag2 = diag1;
        dfs(n, 0);
        return ans;
    }
};

时间复杂度: O ( N ! ) O (N!) O(N!)
空间复杂度: O ( N 2 ) O (N²) O(N2)


36. 有效的数独

题目链接:https://leetcode.cn/problems/valid-sudoku/description/
在这里插入图片描述


优质解

思路:

  • 主要的难点还是剪枝,行、列、3 * 3的宫内
  • 行和列,我们都可以选择开一个9 * 9bool数组来代表,第i行内1 - 9有没有出现过
  • 那3 * 3 的宫内的?总共有 9 个宫,我们也可以 开一个9 * 9每一行代表一个宫,可是这很难和实际的物理位置映射上,很难描述
  • 我们不妨把一个3 * 3看成一个元素,这样我们可以开一个3 * 3的数组,里面每个元素又是一个记录1 - 9的数字有没有在宫内出现过的bool数组,即:开一个3 * 3 * 9
  • 并且对应关系为:行号 / 3(的整数) == 对应宫的行号,列号同理

代码:

class Solution {
public:
    bool row[9][10];
    bool col[9][10];
    bool palace[3][3][10];
    bool isValidSudoku(vector<vector<char>>& board) 
    {
        for(int i = 0; i < 9; i++)
        {
            for(int j = 0; j < 9; j++)
            {
                if(board[i][j] == '.')
                    continue;
                int num = board[i][j] - '0';
                if(row[i][num] || col[j][num] || palace[i / 3][j / 3][num])
                    return false;
                else
                {
                    row[i][num] = true;
                    col[j][num] = true;
                    palace[i / 3][j / 3][num] = true;
                }
            }
        }
        return true;
    }
};

时间复杂度: O ( 1 ) O(1) O(1)
空间复杂度: O ( 1 ) O(1) O(1)
, 空间换时间


37. 解数独

题目链接:https://leetcode.cn/problems/sudoku-solver/description/

在这里插入图片描述


优质解

思路:

  • 每个位置都尝试填 1 - 9,然后能不能填入的判断方法和上一题一样
  • 利用返回值来进行剪枝

代码:

class Solution {
public:
    bool row[9][10];
    bool col[9][10];
    bool palace[3][3][10];
    bool dfs(vector<vector<char>>& board) // 用返回值来剪枝
    {
        for(int i = 0; i < 9; i++)
        {
            for(int j = 0; j < 9; j++)
            {
                if(board[i][j] == '.')
                {
                    for(int num = 1; num <= 9; num++)
                    {
                        if(!row[i][num] && !col[j][num] && !palace[i / 3][j / 3][num])
                        {
                            board[i][j] = num + '0';
                            row[i][num] = col[j][num] = palace[i / 3][j / 3][num]  = true;
                            // 只有子问题都填正确了,这里才能返回true
                            if(dfs(board) == true) return true; 
                            // 填错的那一层被返回,重新填这一个位置
                            board[i][j] = '.';
                            row[i][num] = col[j][num] = palace[i / 3][j / 3][num]  = false;
                        }
                    }
                    return false; // 1 - 9 中没有合适的,则代表前面的填法肯定错误(于是就会层层返回,返回到填错的那一层)
                }
            }
        }
        return true;
    }
    
    void solveSudoku(vector<vector<char>>& board) 
    {
        for(int i = 0; i < 9; i++)
        {
            for(int j = 0; j < 9; j++)
            {
                if(board[i][j] != '.')
                {
                    int num = board[i][j] - '0';
                    row[i][num] = col[j][num] = palace[i / 3][j / 3][num]  = true;
                }
            }
        }
        dfs(board);
    }
};

时间复杂度: O ( 9 m ) O (9^m) O(9m),其中 m 是数独中空白格子的数量
空间复杂度: O ( 1 ) O(1) O(1)


🌈我的分享也就到此结束啦🌈
要是我的分享也能对你的学习起到帮助,那简直是太酷啦!
若有不足,还请大家多多指正,我们一起学习交流!
📢公主,王子:点赞👍→收藏⭐→关注🔍
感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

愚润泽

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

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

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

打赏作者

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

抵扣说明:

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

余额充值