【算法篇】回溯算法类(2)(笔记)

news2024/11/25 14:46:50

目录

一、LeetCode 题目

1. 子集II

2. 递增子序列

3. 全排列

4. 全排列 II

5. 重新安排行程

6. N皇后

7. 解数独

二、题目思路整理


一、LeetCode 题目

1. 子集II

https://leetcode.cn/problems/subsets-ii/description/icon-default.png?t=O83Ahttps://leetcode.cn/problems/subsets-ii/description/

        给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。

示例 1:
输入:nums = [1,2,2]
输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]

示例 2:
输入:nums = [0]
输出:[[],[0]]

// 方法一:
class Solution {
public:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(vector<int>& nums, int startIndex) {
        result.push_back(path);
        for (int i = startIndex; i < nums.size(); i++) {
            if (i > startIndex && nums[i] == nums[i - 1]) {
                continue;
            }
            path.push_back(nums[i]);
            backtracking(nums, i + 1);
            path.pop_back();
        }
        return;
    }

    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        backtracking(nums, 0);
        return result;
    }
};


// 方法二:(unordered_set 的使用)
class Solution {
public:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(vector<int>& nums, int startIndex) {
        result.push_back(path);

        unordered_set<int> uset;
        for (int i = startIndex; i < nums.size(); i++) {
            if (uset.find(nums[i]) != uset.end()) {
                continue;
            }
            uset.insert(nums[i]);
            path.push_back(nums[i]);
            backtracking(nums, i + 1);
            path.pop_back();
        }
        return;
    }

    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        backtracking(nums, 0);
        return result;
    }
};

2. 递增子序列

https://leetcode.cn/problems/non-decreasing-subsequences/description/icon-default.png?t=O83Ahttps://leetcode.cn/problems/non-decreasing-subsequences/description/

        给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。

        数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。

示例 1:
输入:nums = [4,6,7,7]
输出:[[4,6],[4,6,7],[4,6,7,7],[4,7],[4,7,7],[6,7],[6,7,7],[7,7]]

示例 2:
输入:nums = [4,4,3,2,1]
输出:[[4,4]]

class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(vector<int>& nums, int startIndex) {
        if (path.size() > 1) {
            result.push_back(path);
        }
        unordered_set<int> uset; // 使用set对本层元素进行去重
        for (int i = startIndex; i < nums.size(); i++) {
            if ((!path.empty() && nums[i] < path.back())
                || uset.find(nums[i]) != uset.end()) {
                continue;
            }
            uset.insert(nums[i]); // 记录这个元素在本层用过了,本层后面不能再用了
            path.push_back(nums[i]);
            backtracking(nums, i + 1);
            path.pop_back();
        }
    }
public:
    vector<vector<int>> findSubsequences(vector<int>& nums) {
        backtracking(nums, 0);
        return result;
    }
};

3. 全排列

https://leetcode.cn/problems/permutations/description/icon-default.png?t=O83Ahttps://leetcode.cn/problems/permutations/description/

         给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

示例 1:
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

示例 2:
输入:nums = [0,1]
输出:[[0,1],[1,0]]

示例 3:
输入:nums = [1]
输出:[[1]]

class Solution {
public:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(vector<int>& nums, vector<bool>& used) {
        if (path.size() == nums.size()) {
            result.push_back(path);
            return;
        }
        for (int i = 0; i < nums.size(); i++) {
            if (used[i] == true) {
                continue;
            }
            path.push_back(nums[i]);
            used[i] = true;
            backtracking(nums, used);
            path.pop_back();
            used[i] = false;
        }
        return;
    }

    vector<vector<int>> permute(vector<int>& nums) {
        vector<bool> used(nums.size(), false);
        backtracking(nums, used);
        return result;
    }
};

4. 全排列 II

https://leetcode.cn/problems/permutations-ii/description/icon-default.png?t=O83Ahttps://leetcode.cn/problems/permutations-ii/description/

        给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。

示例 1:
输入:nums = [1,1,2]
输出:
[[1,1,2],
 [1,2,1],
 [2,1,1]]

示例 2:
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
class Solution {
public:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(vector<int>& nums, vector<bool>& used) {
        if (path.size() == nums.size()) {
            result.push_back(path);
            return;
        }
        for (int i = 0; i < nums.size(); i++) {
            if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {  
                // 如果上一个数还没用,那么选择第i个和上一个没什么区别
                continue;
            }
            if (used[i] == false) {
                path.push_back(nums[i]);
                used[i] = true;
                backtracking(nums, used);
                path.pop_back();
                used[i] = false;
            }
        }
        return;
    }

    vector<vector<int>> permuteUnique(vector<int>& nums) {
        vector<bool> used(nums.size(), false);
        sort(nums.begin(), nums.end());
        backtracking(nums, used);
        return result;
    }
};

        树层上去重(used[ i - 1 ] == false),的树形结构如下:


        树枝上去重(used[ i - 1 ] == true)的树型结构如下:

5. 重新安排行程

https://leetcode.cn/problems/reconstruct-itinerary/description/icon-default.png?t=O83Ahttps://leetcode.cn/problems/reconstruct-itinerary/description/

        给你一份航线列表 tickets ,其中 tickets[i] = [fromi, toi] 表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。

        所有这些机票都属于一个从 JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 开始。如果存在多种有效的行程,请你按字典排序返回最小的行程组合。

  • 例如,行程 ["JFK", "LGA"] 与 ["JFK", "LGB"] 相比就更小,排序更靠前。

        假定所有机票至少存在一种合理的行程。且所有的机票 必须都用一次 且 只能用一次。

示例 1:
输入:tickets = [["MUC","LHR"],["JFK","MUC"],["SFO","SJC"],["LHR","SFO"]]
输出:["JFK","MUC","LHR","SFO","SJC"]

示例 2:
输入:tickets = [["JFK","SFO"],["JFK","ATL"],["SFO","ATL"],["ATL","JFK"],["ATL","SFO"]]
输出:["JFK","ATL","JFK","SFO","ATL","SFO"]
解释:另一种有效的行程是 ["JFK","SFO","ATL","JFK","ATL","SFO"] ,但是它字典排序更大更靠后。
class Solution {
private:
// unordered_map<出发机场, map<到达机场, 航班次数>> targets
unordered_map<string, map<string, int>> targets;
bool backtracking(int ticketNum, vector<string>& result) {
    if (result.size() == ticketNum + 1) {
        return true;
    }
    for (pair<const string, int>& target : targets[result[result.size() - 1]]) {
        if (target.second > 0 ) { // 记录到达机场是否飞过了
            result.push_back(target.first);
            target.second--;
            if (backtracking(ticketNum, result)) return true;
            result.pop_back();
            target.second++;
        }
    }
    return false;
}
public:
    vector<string> findItinerary(vector<vector<string>>& tickets) {
        targets.clear();
        vector<string> result;
        for (const vector<string>& vec : tickets) {
            targets[vec[0]][vec[1]]++; // 记录映射关系
        }
        result.push_back("JFK"); // 起始机场
        backtracking(tickets.size(), result);
        return result;
    }
};

6. N皇后

https://leetcode.cn/problems/n-queens/description/icon-default.png?t=O83Ahttps://leetcode.cn/problems/n-queens/description/

        按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

        给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。

示例 1:
输入:n = 4
输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
解释:如上图所示,4 皇后问题存在两个不同的解法。

示例 2:
输入:n = 1
输出:[["Q"]]

class Solution {
public:
    vector<vector<string>> result;
    void backtracking(int n, int row, vector<string>& chessboard) {
        if (row == n) {
            result.push_back(chessboard);
            return;
        }
        for (int col = 0; col < n; col++) {   // 每一行的不同位置
            if (isValid(row, col, chessboard, n)) {
                chessboard[row][col] = 'Q';
                backtracking(n, row + 1, chessboard);
                chessboard[row][col] = '.';
            }
        }
        return;
    }

    bool isValid(int row, int col, vector<string>& chessboard, int n) {
        // 比较列
        for (int i = 0; i < row; i++) {
            if (chessboard[i][col] == 'Q') return false;
        }
        // 比较45度
        for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
            if (chessboard[i][j] == 'Q') return false;
        }
        // 比较135度
        for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
            if (chessboard[i][j] == 'Q') return false;
        }
        return true;
    }

    vector<vector<string>> solveNQueens(int n) {
        vector<string> chessboard(n, string(n, '.'));
        backtracking(n, 0, chessboard);
        return result;
    }
};

7. 解数独

https://leetcode.cn/problems/sudoku-solver/description/icon-default.png?t=O83Ahttps://leetcode.cn/problems/sudoku-solver/description/

        编写一个程序,通过填充空格来解决数独问题。

数独的解法需 遵循如下规则:

  1. 数字 1-9 在每一行只能出现一次。
  2. 数字 1-9 在每一列只能出现一次。
  3. 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)

        数独部分空格内已填入了数字,空白格用 '.' 表示。

示例 1:
输入:board = [["5","3",".",".","7",".",".",".","."],["6",".",".","1","9","5",".",".","."],[".","9","8",".",".",".",".","6","."],["8",".",".",".","6",".",".",".","3"],["4",".",".","8",".","3",".",".","1"],["7",".",".",".","2",".",".",".","6"],[".","6",".",".",".",".","2","8","."],[".",".",".","4","1","9",".",".","5"],[".",".",".",".","8",".",".","7","9"]]
输出:[["5","3","4","6","7","8","9","1","2"],["6","7","2","1","9","5","3","4","8"],["1","9","8","3","4","2","5","6","7"],["8","5","9","7","6","1","4","2","3"],["4","2","6","8","5","3","7","9","1"],["7","1","3","9","2","4","8","5","6"],["9","6","1","5","3","7","2","8","4"],["2","8","7","4","1","9","6","3","5"],["3","4","5","2","8","6","1","7","9"]]
解释:输入的数独如上图所示,唯一有效的解决方案如下所示:

思路:

        一个 for 循环遍历棋盘的行,一个for循环遍历棋盘的列,一行一列确定下来之后,递归遍历这个位置放9个数字的可能性

// 写法一:
class Solution {
public:
    bool backtracking(vector<vector<char>>& board) {
        for (int i = 0; i < board.size(); i++) {        // 遍历行
            for (int j = 0; j < board[0].size(); j++) { // 遍历列
                if (board[i][j] == '.') {
                    for (char k = '1'; k <= '9'; k++) { 
                        if (isValid(board, i, j, k)) {
                            board[i][j] = k;
                            if (backtracking(board)) return true; // 如果找到合适一组立刻返回
                            board[i][j] = '.';
                        }
                    }
                    return false;  // 9个数都试完了,都不行,返回false
                }
            }
        }
        return true; // 遍历完没有返回false,说明找到了合适棋盘位置了
    }

    bool isValid(vector<vector<char>>& board, int row, int col, char key) {
        // 检查行
        for (int i = 0; i < 9; i++) {
            if (board[row][i] == key) return false;
        }
        // 检查列
        for (int i = 0; i < 9; i++) {
            if (board[i][col] == key) return false;
        }
        // 检查四方格
        int rows = row / 3;
        int cols = col / 3;
        for (int i = 3 * rows; i < 3 * rows + 3; i++) {
            for (int j = 3 * cols; j < 3 * cols + 3; j++) {
                if (board[i][j] == key) return false;
            }
        }
        return true;
    }

    void solveSudoku(vector<vector<char>>& board) {
        backtracking(board);
        return;
    }
};


// 写法二:
class Solution {
public:
    bool backtracking(vector<vector<char>>& board) {
        for (int i = 0; i < board.size(); i++) {        // 遍历行
            for (int j = 0; j < board[0].size(); j++) { // 遍历列
                if (board[i][j] == '.') {   // 递归的上一层已经填上一个数字了,所以这里这里直接跳过了
                    for (char k = '1'; k <= '9'; k++) { 
                        if (isValid(board, i, j, k)) {
                            board[i][j] = k;
                            if (backtracking(board)) return true; // 如果找到合适一组立刻返回
                            board[i][j] = '.';
                        }
                    }
                    return false;  // 9个数都试完了,都不行,返回false
                }
            }
        }
        return true; // 遍历完没有返回false,说明找到了合适棋盘位置了
    }

    bool isValid(vector<vector<char>>& board, int row, int col, char key) {
        // 检查行
        for (int i = 0; i < 9; i++) {
            if (board[row][i] == key) return false;
        }
        // 检查列
        for (int i = 0; i < 9; i++) {
            if (board[i][col] == key) return false;
        }
        // 检查四方格
        int rows = row / 3;
        int cols = col / 3;
        for (int i = 3 * rows; i < 3 * rows + 3; i++) {
            for (int j = 3 * cols; j < 3 * cols + 3; j++) {
                if (board[i][j] == key) return false;
            }
        }
        return true;
    }

    void solveSudoku(vector<vector<char>>& board) {
        backtracking(board);
        return;
    }
};

二、题目思路整理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2193383.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring Cloud Netflix Eureka 注册中心讲解和案例示范

在微服务架构中&#xff0c;服务的发现和注册是至关重要的一环。Netflix Eureka 是一个在云端设计的服务注册与发现系统。它允许各个微服务将自身注册到注册中心&#xff0c;并在需要时发现其他服务&#xff0c;从而实现客户端负载均衡、服务容错以及动态扩展。本文将深入分析 …

【Vue】vue-admin-template项目搭建

准备 node环境 node&#xff1a;v16.12.0npm&#xff1a;8.1.0 vue-element-admin下载 官网&#xff1a;https://panjiachen.github.io/vue-element-admin-site/guide/ 我这边下载的是4.4.0版本的&#xff0c;使用其他版本可能会因为所需要的node和npm版本过低或过高导致异常…

C++/CLI编程-属性property的封装实现

1.前言 上一篇博文中提到过C/CLI典型使用场景之一是为native C编写的算法封装接口&#xff0c;进而在C#项目中调用。一种典型的应用是作为Wrapper封装层来封装C库的类和接口、做C/C#类型的转换&#xff0c;然后在C#应用程序中调用&#xff0c;开发便捷易用的应用程序。 C/CLI编…

Bellman-Ford算法和SPFA算法

Bellman-Ford算法 能够处理存在负边权的情况。 算法时间复杂度:O(n*m)&#xff0c;n是顶点数&#xff0c;m是边数。 算法实现: 设s为起点&#xff0c;dis[v]即为s到v的最短距离&#xff0c;pre[v]为v前驱。w[j]是边j的长度&#xff0c;且j连接u、v。 dis[s] 0;dis[v] 0x3…

(Linux驱动学习 - 5).Linux 下 DHT11 温湿度传感器驱动编写

DHT11的通信协议是单总线协议&#xff0c;可以用之前学习的pinctl和gpio子系统完成某IO引脚上数据的读与写。 一.在设备树下添加dht11的设备结点 1.流程图 2.设备树代码 &#xff08;1&#xff09;.在设备树的 iomuxc结点下添加 pinctl_dht11 &#xff08;2&#xff09;.在根…

探索大型语言模型在文化常识方面的理解能力与局限性

介绍 论文地址&#xff1a;https://arxiv.org/pdf/2405.04655v1 近年来&#xff0c;大型语言模型&#xff08;LLM&#xff09;不仅被广泛应用于各个领域&#xff0c;而且通过大量的基准评估&#xff0c;证明它们能够理解人类所拥有的常识&#xff08;Commonsense&#xff09;…

FLUX的ID保持项目也来了! 字节开源PuLID-FLUX-v0.9.0,开启一致性风格写真新纪元!

之前的文章已经和大家介绍过字节开源的ID保持项目PuLID。随着FLUX模型的发布&#xff0c;PuLID也开源了 FLUX 版本的模型&#xff0c;不得不说FLUX的强大&#xff0c;两个月生态就赶上了SDXL。这次新发布PuLID-FLUX-v0.9.0模型&#xff0c;它为FLUX.1-dev提供了无需调整的ID定制…

简单部署vue+springboot项目

vue 参考博客 先将vue项目打包 npm run build 再创建项目文件夹front,在front中新建nginx.conf server {listen 80;server_name localhost;# 请求体的大小限制client_max_body_size 50m;# 日志文件存放地址access_log /var/log/nginx/host.access.log main;error…

嵌入式知识点复习(一)

国庆倒数第二天&#xff0c;进行嵌入式课堂测试的复习&#xff1a; 第一章 绪论 1.1 嵌入式系统的概念 嵌入式系统定义 嵌入式系统定位 嵌入式系统形式 嵌入式系统三要素 嵌入式系统与桌面通用系统的区别 1.2 嵌入式系统的发展历程 微处理器的演进历史 单片机的演进历史 …

学习使用Cube软件

一、点亮LED灯 1、新建项目 File → New → STM32 Project搜索芯片信号项目名称 弹窗点击Yes 2、点亮LED 配置GPIO为输出模式 细化配置 保存&#xff08;ctrl S&#xff09;自动生成代码 手动生成代码 选择跳转到代码页面

【d60】【Java】【力扣】509. 斐波那契数

思路 要做的问题&#xff1a;求F&#xff08;n&#xff09;, F&#xff08;n&#xff09;就等于F(n-1)F(n-2)&#xff0c;要把这个F(n-1)F(n-2)当作常量&#xff0c;已经得到的值&#xff0c; 结束条件&#xff1a;如果是第1 第2 个数字的时候&#xff0c;没有n-1和n-2,所以…

系统设计,如何设计一个秒杀功能

需要解决的问题 瞬时流量的承接防止超卖预防黑产避免对正常服务的影响兜底方法 前端设计 利用 CDN 缓存静态资源&#xff0c;减轻服务器的压力在前端随机限流按钮防抖&#xff0c;防止用户重复点击 后端设计 Nginx 做统一接入&#xff0c;进行负载均衡与限流用 sentinel 等…

Kron Reduction消去法如何操作,矩阵推导过程

三阶矩阵消去单节点 在电力系统中,母线上的电流注入始终为0,这样的节点可以通过一定的方法消除。以三节点为例,假设注入节点3的电流为0,则: [ I 1 I 2 I 3 ] = [ I 1 I 2 0 ] = [ Y 11 Y 12 Y 13 Y 21 Y 22 Y 23 Y 31 Y 32 Y 33 ] [ V 1 V 2 V 3 ] \left[\begin{array}{…

交叉熵的数学推导和手撕代码

交叉熵的数学推导和手撕代码 数学推导手撕代码 数学推导 手撕代码 import torch import torch.nn.functional as F# 二元交叉熵损失函数 def binary_cross_entropy(predictions, targets):# predictions应为sigmoid函数的输出&#xff0c;即概率值# targets应为0或1的二进制标…

一个值得关注的3D生成新算法:速度和图像生成平齐,能生成合理的展开贴图和高质量mesh

今天跟大家介绍的GIMDiffusion是一种新的Text-to-3D模型&#xff0c;利用几何图像&#xff08;Geometry Images&#xff09;来高效地表示3D形状&#xff0c;避免了复杂的3D架构。通过结合现有的Text-to-Image模型如Stable Diffusion的2D先验知识&#xff0c;GIMDiffusion能够在…

系统架构设计师论文《论NoSQL数据库技术及其应用》精选试读

论文真题 随着互联网web2.0网站的兴起&#xff0c;传统关系数据库在应对web2.0 网站&#xff0c;特别是超大规模和高并发的web2.0纯动态SNS网站上已经显得力不从心&#xff0c;暴露了很多难以克服的问题&#xff0c;而非关系型的数据库则由于其本身的特点得到了非常迅速的发展…

LeetCode讲解篇之139. 单词拆分

文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 我们使用一个数组记录字符串s在[0, i)区间能否使用wordDict组成 我们使用左右指针遍历字符串s的子串&#xff0c;左指针 j 为子串的左端点下标&#xff0c;右指针 i 为右端点下标的下一个 遍历过程中如果字符串s…

利士策分享,哀牢山:网红打卡地背后的探险风险

利士策分享&#xff0c;哀牢山&#xff1a;网红打卡地背后的探险风险 最近&#xff0c;云南的哀牢山因其独特的自然风光和神秘探险氛围而迅速走红网络。许多游客&#xff0c;特别是户外探险爱好者&#xff0c;纷纷涌入这片神秘的山脉&#xff0c;想要亲身体验那份原始与野性的…

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-06

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-06 目录 文章目录 计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-06目录1. A LLM-Powered Automatic Grading Framework with Human-Level Guidelines Optimization摘要&#xff1a;研究背景&…