IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 【力扣刷题】Day24——回溯专题 -> 正文阅读

[数据结构与算法]【力扣刷题】Day24——回溯专题


回溯专题到这就暂时完结啦,后续再多扩展刷题吧,上一篇文章:【力扣刷题】Day23——回溯专题_塔塔开!!!的博客-CSDN博客

四、排列问题

11. 全排列

题目链接:全排列

思路:模板题,dfs实现排列型枚举

Code

class Solution {
    int N = 25;
    int n;
    List<List<Integer>> res = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    boolean[] st = new boolean[N];
    public List<List<Integer>> permute(int[] nums) {
        n = nums.length;
        for(int i = 0; i < n; i ++) nums[i] += 10;// 防止数组下标为负数
        dfs(nums, 0);
        return res;
    }
    public void dfs(int[] nums, int u){
        if(u == n){
            res.add(new ArrayList(path));
            return ;
        }
        // 枚举每一种可能
        for(int i = 0; i < n; i ++){
            if(!st[nums[i]]){// 如果是这样子判断的话,nums[i]可能为负数,但是数组下标是不能为负的,为此要扩大
                st[nums[i]] = true;
                path.add(nums[i] - 10);
                dfs(nums, u + 1);
                st[nums[i]] = false;
                path.remove(path.size() - 1);// 回溯
            }
        }
    }
}

另一种写法(该位置上的数,用位置来判断是否选过)

Code

class Solution {
    int N = 25;
    int n;
    List<List<Integer>> res = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    boolean[] st = new boolean[N];
    public List<List<Integer>> permute(int[] nums) {
        n = nums.length;
        dfs(nums, 0);
        return res;
    }
    public void dfs(int[] nums, int u){
        if(u == n){
            res.add(new ArrayList(path));
            return ;
        }
        // 枚举每一种可能
        for(int i = 0; i < n; i ++){
            if(!st[i]){
                st[i] = true;
                path.add(nums[i]);
                dfs(nums, u + 1);
                st[i] = false;
                path.remove(path.size() - 1);
            }
        }
    }
}

12. 全排列 II

题目链接:47. 全排列 II - 力扣(LeetCode)

排列I枚举的话就会出现重复集合:

在这里插入图片描述

本题与上一题的区别:数组存在重复元素,要对结果集进行去重:

  • 排序
  • 重复的元素每次只以第一个元素为准即可,以第一个重复的元素去递归访问即可,后面的重复元素遇到直接跳过(剪枝)

Code

class Solution {
    int N = 25;
    int n;
    List<List<Integer>> res = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    boolean[] st = new boolean[N];
    public List<List<Integer>> permuteUnique(int[] nums) {
        n = nums.length;
        Arrays.sort(nums);
        dfs(nums, 0);
        return res;
    }
    public void dfs(int[] nums, int u){
        if(u == n){
            res.add(new ArrayList(path));
            return ;
        }
        // 枚举每一种可能
        for(int i = 0; i < n; i ++){
            
            // 剪枝(跳过重复的枝条)
            if(i > 0 && nums[i] == nums[i - 1] && st[i - 1] == true) continue;

            if(!st[i]){
                st[i] = true;
                path.add(nums[i]);
                dfs(nums, u + 1);
                st[i] = false;
                path.remove(path.size() - 1);
            }
        }
    }
}

五、棋盘问题

13. N 皇后

题目链接:51. N 皇后 - 力扣(LeetCode)

思路:N皇后模板题

Code

class Solution {
    int N = 25;
    boolean[] col = new boolean[N];
    boolean[] dg = new boolean[N];
    boolean[] udg = new boolean[N];
    char[][] g = new char[N][N];
    int n;
    List<List<String>> res = new ArrayList<>();
    public List<List<String>> solveNQueens(int _n) {
       n = _n;
       for(int i = 0; i < n; i ++){// 棋盘初始化
           for(int j = 0; j < n; j ++){
               g[i][j] = '.';
           }
       } 
       dfs(0);// 从第0行开始
       return res;
    }
    public void dfs(int u){
        if(u == n){
            List<String> tmp = new ArrayList<>();
            String str = "";
            for(int i = 0; i < n; i ++){
                for(int j = 0; j < n; j ++){
                    str += g[i][j];
                }
                tmp.add(str);// 加入棋盘的每一行
                str = "";// 为下一次做好准备
            }
            res.add(new ArrayList(tmp));
            return ;
        }
        // 枚举所有列
        for(int i = 0; i < n; i ++){
            if(!col[i] && !dg[u + i] && !udg[u - i + n]){
                col[i] = dg[u + i] = udg[u - i + n] = true;
                g[u][i] = 'Q';
                dfs(u + 1);
                col[i] = dg[u + i] = udg[u - i + n] = false;
                g[u][i] = '.';
            }
        }
    }
}

14. N皇后 II

题目链接:52. N皇后 II - 力扣(LeetCode)

思路:N皇后模板题

Code

class Solution {
    int N = 25;
    boolean[] col = new boolean[N];
    boolean[] dg = new boolean[N];
    boolean[] udg = new boolean[N];
    char[][] g = new char[N][N];
    int n;
    int res = 0;

    public int totalNQueens(int _n) {
       n = _n;
       for(int i = 0; i < n; i ++){
           for(int j = 0; j < n; j ++){
               g[i][j] = '.';
           }
       } 
       dfs(0);// 从第0行开始
       return res;
    }
    public void dfs(int u){
        if(u == n){
            res ++;
            return ;
        }
        // 枚举所有列
        for(int i = 0; i < n; i ++){
            if(!col[i] && !dg[u + i] && !udg[u - i + n]){
                col[i] = dg[u + i] = udg[u - i + n] = true;
                g[u][i] = 'Q';
                dfs(u + 1);
                col[i] = dg[u + i] = udg[u - i + n] = false;
                g[u][i] = '.';
            }
        }
    }
}

15. 解数独

题目链接:37. 解数独 - 力扣(LeetCode)

Code

思路:和N皇后的思路很像,只是扩展到了二维递归。

从前往后枚举每一个空格该填哪一个数。

状态:row[9][9]col[9][9]cell[3][3][9]

每一行、每一列、每一个九宫格里边只能填不一样的数字,判断是否可以选:

row[9][9]row[i][x],当前第i行选的是哪一个数字(选的数字x

col[9][9]col[i][x],当前第i列选的是哪一个数字(选的数字x

cell[3][3][9]cell[i][j][x],九宫格中(i, j)位置填的数为x

Code

class Solution {
    boolean[][] row = new boolean[10][10];
    boolean[][] col = new boolean[10][10];
    boolean[][][] cell = new boolean[3][3][10];

    public void solveSudoku(char[][] g) {
        // 初始化
        for(int i = 0; i < 9; i ++){
            for(int j = 0; j < 9; j ++){
                if(g[i][j] != '.'){// 不是空格(题目填好的数字)
                    int t = g[i][j] - '0';
                    row[i][t] = col[j][t] = cell[i / 3][j / 3][t] = true;// 标记已用过
                }
            }
        }
        dfs(g, 0, 0);
    }
    public boolean dfs(char[][] g, int x, int y){
        // 判断边界
        if(y == 9){
            x ++;
            y = 0;
            if(x == 9) return true;
        }
        if(g[x][y] != '.') return dfs(g, x, y + 1);// 若当前数已经填过了,就跳过

        // 枚举所有可能情况
        for(int val = 1; val <= 9; val ++){
            if(!row[x][val] && !col[y][val] && !cell[x / 3][y / 3][val]){
                g[x][y] = (char)('0' + val);// 转为字符
                row[x][val] = col[y][val] = cell[x / 3][y / 3][val] = true;
                if(dfs(g, x, y + 1)) return true;// 填下一个数
                g[x][y] = '.';// 恢复现场
                row[x][val] = col[y][val] = cell[x / 3][y / 3][val] = false;
            }
        }
        return false;
    } 
}

六、其他

16. 递增子序列

题目链接:491. 递增子序列 - 力扣(LeetCode)

思路:递归回溯枚举所有组合:

  • 注意去重(由于不能排序,那么我们用之前的去重条件剪枝,只能达到部分去重的目的,结果集仍然可能存在重复集合,如1 3 4 1 1),当枚举到这两个1时,结果集[1, 1]还是会重复的,为了达到去重的目的,我们可以用一个Set集合来记录结果集。
  • 要保证选择的序列是递增的

Code

class Solution {
    HashSet<List<Integer>> res = new HashSet<>();
    List<Integer> path = new ArrayList<>();
    int n;
    public List<List<Integer>> findSubsequences(int[] nums) {
        n = nums.length;
        dfs(nums, 0);
        return new ArrayList<List<Integer>>(res);
    }
    public void dfs(int[] nums, int satrt){
        if(path.size() >= 2){
            res.add(new ArrayList(path));
            // 这里不能return , 答案有多种不能直接结束递归了
        }

        // 枚举所有可能
        for(int i = satrt; i < n; i ++){
            // 去重剪枝
            if(i > satrt && nums[i] == nums[i - 1]) continue;// 但是并不能达到完全重重的目的(因为我们没有排序(不能排序),结果集仍然会有重复的)
            // 保证序列是递增的
            if(path.size() > 0 && nums[i] < path.get(path.size() - 1)) continue;

            path.add(nums[i]);
            dfs(nums, i + 1);
            path.remove(path.size() - 1);
        }
    }
}

17. 重新安排行程

题目链接:332. 重新安排行程 - 力扣(LeetCode)

思路:这题很像是一个图,找到一个路线形成图,我们依然可以使用递归回溯,枚举所有方案,找到一条符合答案的路线,由于可能存在多组解,但题目要求我们在多组解的情况下取字典序最小的解,每一次递归枚举时如何获得字典序最小得城市呢?

  • 由于一个form可能对应多个to,我们用map存储信息时,value(to)用一个优先队列来维护(默认小根堆)即得到字典序最小得这段路线信息了!
  • from找到des就一直递归,直到最终找完完整路线(答案),递归结束回溯得时候加入res集合即可,翻转后即为答案所求路线!

Code

class Solution {
    Map<String, PriorityQueue<String>> mp = new HashMap<>();
    List<String> res = new ArrayList<>();
    public List<String> findItinerary(List<List<String>> tickets) {
        // 存信息
        for(List<String> tic : tickets){
            String src = tic.get(0), des = tic.get(1);
            if(!mp.containsKey(src)) mp.put(src, new PriorityQueue<String>());
            mp.get(src).offer(des);// 目的地加入优先队列
        }
        dfs("JFK");
        Collections.reverse(res);
        return res;
    }
    public void dfs(String s){
        
        // 枚举路线
        while(mp.containsKey(s) && mp.get(s).size() >= 1){
            String des = mp.get(s).poll();// 拿到字典序最小得des
            dfs(des);
        }
        res.add(s);// 找到完整路线(递归结束回溯的时候甜的,最后要将路线翻转才得到答案路线)
    }
}
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-10-17 12:59:49  更:2022-10-17 13:03:13 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 19:49:03-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码