当前位置: 首页 > article >正文

代码随想录day25 回溯4

题目:491.递增子序列   46全排列   47全排列II

补充:332.重新安排行程   51.N皇后   37.解数独

需要重做:

491.递增子序列

思路:

1.本题需要在原有的顺序上进行,不能排序。而且对同一父节点下,本层使用过的元素在后面不能再使用,因为同样的值可能是分开的,所以考虑用哈希表记录。

2.因为本题的数字大小确定,考虑用数组记录。映射值=值+100.

3.这个数组不需要回溯,因为for里面的每一层都是横向的,只有纵向(backtracking)后才需要回溯,回溯到没有加入这个元素之前。

注意:for里面,最好是if里面写continue,否则容易写不全。

代码:

class Solution {
public:
    vector<vector<int>>res;
    vector<int>path;
    void backtracking(vector<int>&nums,int startIndex){
        if(path.size()>=2)res.push_back(path);
        //同一父节点下,本层的使用了a,别的就不能再使用a了
        //所以用unordered_set去重,固定范围所以用数组.-100->100,映射的时候加100即可
        int a[201]={0};
        for(int i=startIndex;i<nums.size();i++){
            if((path.size()!=0&&nums[i]<path.back())||a[nums[i]+100]==1){
                continue;
            }
                path.push_back(nums[i]);
                a[nums[i]+100]=1;
                backtracking(nums,i+1);
                path.pop_back();
        }
    }
    vector<vector<int>> findSubsequences(vector<int>& nums) {
        res.clear();
        path.clear();
        backtracking(nums,0);
        return res;
    }
};

46.全排列

思路:

1.排列问题,不需要startIndex,只需要一个used数组记录是否使用过。

2.停止条件:如果path的size==nums,return

4.单层逻辑:如果用过的,则continue;没用过则加入,改used值,递归,回溯

注意:

代码:

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

47.全排列 II

思路:

1.在排列的基础上,去除同一父节点下的重复的

2.停止条件还是path的size跟nums的相等

3.单层逻辑:

如果是竖着的已经使用过的,continue

如果是横着使用过的,continue

否则正常逻辑

注意:需要先排列才可以这样用!!如果不能排列的就要用unordered_set代替

代码:

class Solution {
public:
    vector<vector<int>>res;
    vector<int>path;
    //去重逻辑:同一父节点的本层用过了一个,后面的就不能用了
    void backtracking(vector<int>&nums,vector<bool>used){
        if(path.size()==nums.size()){
            res.push_back(path);
            return;
        }
        for(int i=0;i<nums.size();i++){
            if(used[i]==true)continue;
            if(i>0&&nums[i]==nums[i-1]&&used[i-1]==false)continue;
            path.push_back(nums[i]);
            used[i]=true;
            backtracking(nums,used);
            used[i]=false;
            path.pop_back();
        }
    }
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        res.clear();
        path.clear();
        sort(nums.begin(),nums.end());
        vector<bool>used(nums.size(),false);
        backtracking(nums,used);
        return res;
    }
};

332.重新安排行程(可跳过)

思路:

分析题目:我们需要一个能表示机票的数据结构,这个机票要包含开始地,目的地,到这个目的地的机票数量。所以考虑:unordered_map<string,map<string,int>>

含义:unordered_map<起始地,map<目的地,起始到目的地的机票张数>>

终止条件:res的值==总机票张数加1

返回值:bool,因为只需要找到排序好的第一条就可以返回了

单层逻辑:

如果这个目的地的机票数大于0,则加入,记录,递归,回溯。

注意这里,if(backtracking(ticketsNum))return true;如果这条路是对的,则返回,否则在for的外面return false;

注意:遍历的写法:

for(pair <const string,int>&t :target[res[res.size()-1]])

 for(const vector<string>&vec :tickets)

且主函数中需要初始化target和res

代码:

class Solution {
public:
    //含义:出发机场,《到达机场,几张到达这个机场的票》
    unordered_map<string,map<string,int>>target;
    vector<string>res;
    bool backtracking(int ticketsNum){
        if(res.size()==ticketsNum+1)return true;
        for(pair <const string,int>&t :target[res[res.size()-1]]){//即result的最后一个元素,即起始地
            if(t.second>0){
                res.push_back(t.first);
                t.second--;
                //这里需要return true,因为如果这个路是通的,则return true
                if(backtracking(ticketsNum))return true;
                t.second++;
                res.pop_back();
            }
        }
        return false;
    }
    vector<string> findItinerary(vector<vector<string>>& tickets) {
        target.clear();
        res.clear();
        res.push_back("JFK");
        for(const vector<string>&vec :tickets){
            target[vec[0]][vec[1]]++;
        }
        backtracking(tickets.size());
        return res;
    }
};

51.N皇后(适当跳过)

思路:

1.参数类型:void,n,row,chessboard(这个不可以用全局变量,因为对chessboard的初始化需要有n才能做)

2.回溯还是一样思路,注意:递归深度控制行,for里面控制列

如果row==n,则pushback。

单层:遍历col,如果合法,进行操作

3.合法函数:

先对该列进行搜查,然后对45度角,然后对135度角。

注意:

代码:

class Solution {
public:
    vector<vector<string>>res;
    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;
        }
        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;
    }
    void backtracking(int n,int row,vector<string>&chessboard){
        if(row==n){
            res.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]='.';
            }
        }
    }

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

37.解数独(适当跳过)

思路:

1.跟N皇后的思路差不多

2.isValid,判断即可。分为行,列,九宫格

3.只需要找到一个,所以类型为bool,因为主函数的是void,所以在board上改就行。

参数不需要row,col,因为由for循环进行行+列的遍历,每个数都尝试。

注意:backtracking里面每个for循环结束后,是否要return true或者false;

代码:

class Solution {
public:
    bool isValid(int row,int col,char n,vector<vector<char>>&board){
        //行
        for(int i=0;i<9;i++){
            if(board[row][i]==n)return false;
        }
        //列
        for(int i=0;i<9;i++){
            if(board[i][col]==n)return false;
        }
        //九宫格
        int r=(row/3)*3,c=(col/3)*3;
        for(int i=r;i<r+3;i++){
            for(int j=c;j<c+3;j++){
                if(board[i][j]==n)return false;
            }
        }
        return true;

    }
    bool backtracking(vector<vector<char>>&board){
        for(int i=0;i<9;i++){
            for(int j=0;j<9;j++){
                if(board[i][j]!='.')continue;
                for(char k='1';k<='9';k++){
                    if(isValid(i,j,k,board)){
                        board[i][j]=k;
                        if(backtracking(board))return true;
                        board[i][j]='.';
                    }
                }
                return false;//9个数都试过了且没有找到,return false
            }
        }
        return true;
    }

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

总结

看代码随想录总结即可

自己心得:

组合问题:需要n个for循环,横向遍历+纵向。获取的是树叶

        去重逻辑:纵向/横向。善用used数组

切割问题:回文串,IP地址

子集问题:获取树的每个结点

如果要写终止条件,注意:result.push_back(path);要放在终止条件的上面,如下:

result.push_back(path); // 收集子集,要放在终止添加的上面,否则会漏掉结果
if (startIndex >= nums.size()) { // 终止条件可以不加
    return;
}

排列问题;不需要satrtIndex,只需要used数组

去重问题:可以重新排序的,用used。不可以的则用哈希表,能确定范围的用数组更快。


http://www.kler.cn/a/450447.html

相关文章:

  • dns一般设置为多少
  • 14-zookeeper环境搭建
  • 我的 2024 年终总结
  • 【Object字段缺失】JS的对象在更新后发现Key值消失
  • 用 gdbserver 调试 arm-linux 上的 AWTK 应用程序
  • 谷歌浏览器的网络安全检测工具介绍
  • C++ 23版的最新特性
  • WebService简介
  • 建筑工地AI安全检测系统:YOLO11数据标注训练与PyQt5界面设计应用开发
  • 契约锁数智化合同大会全国巡展启动,助力合同管理全程数字化转型
  • 【FAQ】HarmonyOS SDK 闭源开放能力 — Vision Kit(2)
  • 如何打造用户友好的维护页面:6个创意提升WordPress网站体验
  • 一键打断线(根据相交点打断)——CAD c# 二次开发
  • 查询Elasticsearch索引刷新间隔
  • [Unity Shader] 【游戏开发】【图形渲染】Shader数学基础3:矢量与标量的乘法与除法详解
  • IntelliJ IDEA 基本使用教程及Spring Boot项目搭建实战
  • 比亚迪“天神之眼”重磅升级,无图城市领航功能全国开通
  • I.MX6U 启动方式详解
  • mac 使用 launchctl 实现每次登录系统时 frpc 就会自动启动
  • js原型和原型链
  • 实验17 优化算法的比较分析
  • 解决POM依赖与maven仓库关联的问题
  • JAVA HTTP压缩数据
  • 理想很丰满的Ollama-OCR
  • WebSocket | 背景 概念 原理 使用 优缺点及适用场景
  • 单片机:实现动态显示七段数码管(附带源码)