• 【无标题】


    最近无意中看了一些需要使用到「回溯算法」的实例,想起了读书时候的几种类型的提名,时隔多年,做个简单的回顾;

    1.二叉树的遍历

    public class TreeSearchDemo {
       
        
        public static void main(String[] args) {
       
            new TreeSearchDemo().testBinTree1();
        }
    
        public void testBinTree1() {
       
            TreeNode root = makeTestTree();
            List<TreeNode> result = new ArrayList<>();
            preOrder(root, result);
            //inOrder(root, result);
            //postOrder(root, result);
            for (TreeNode item : result) {
       
                System.out.print(item.data);
            }
            System.out.println();
        }
        
        /**
         * 前序遍历
         *
         * @param root
         * @param result
         */
        public void preOrder(TreeNode root, List<TreeNode> result) {
       
            if (root == null) {
       
                return;
            }
            result.add(root);
            preOrder(root.left, result);
            preOrder(root.right, result);
        }
    
        /**
         * 中序遍历
         *
         * @param root
         * @param result
         */
        public void inOrder(TreeNode root, List<TreeNode> result) {
       
            if (root == null) {
       
                return;
            }
            inOrder(root.left, result);
            result.add(root);
            inOrder(root.right, result);
        }
    
        /**
         * 后续遍历
         *
         * @param root
         * @param result
         */
        public void postOrder(TreeNode root, List<TreeNode> result) {
       
            if (root == null) {
       
                return;
            }
            postOrder(root.left, result);
            result.add(root);
            postOrder(root.right, result);
        }
    
        public static class TreeNode {
       
            TreeNode left;
            TreeNode right;
            String data;
        }
    
        private TreeNode makeTestTree() {
       
            TreeNode root = new TreeNode();
            root.data = "A";
            TreeNode nodeB = new TreeNode();
            nodeB.data = "B";
            TreeNode nodeC = new TreeNode();
            nodeC.data = "C";
            TreeNode nodeD = new TreeNode();
            nodeD.data = "D";
            TreeNode nodeE = new TreeNode();
            nodeE.data = "E";
            TreeNode nodeF = new TreeNode();
            nodeF.data = "F";
            //设置节点的关系
            root.left = nodeB;
            root.right = nodeC;
            nodeB.left = nodeD;
            nodeB.right = nodeE;
            nodeE.right = nodeF;
            return root;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103

    构建的二叉树,图示详见:https://blog.csdn.net/nupt123456789/article/details/21193175

    2.二叉树遍历的遍历「路径」

    以「先序」遍历为例,我们如何记录遍历的路径呢?

    • 以先序遍历为例
        public void testBinTreeTracking() {
       
            //构建测试的二叉树
            TreeNode root = makeTestTree();
            //保存遍历的结果
            List<String> result = new ArrayList<>();
            //遍历时搜索的路径
            LinkedList<TreeNode> tracking = new LinkedList<>();
            //保存所有遍历时搜索的路径
            List<LinkedList<TreeNode>> trackingResult = new ArrayList<>();
    
            //先顺遍历先遍历根节点
            tracking.add(root);
            preOrderWithTracking(root, result, tracking, trackingResult);
            //遍历路径
            for (LinkedList<TreeNode> trackingItem : trackingResult) {
       
                for (TreeNode node : trackingItem) {
       
                    if (node != null) {
       
                        System.out.print("->");
                        System.out.print(node.data);
                    }
                    if (node == null) {
       
                        System.out.print("->");
                        System.out.print(" NULL");
                    }
                }
                System.out.println();
            }
        }
    
        /**
         * 先序遍历的遍历路径
         *
         * @param root
         * @param result
         * @param tracking
         * @param trackingResult
         */
        public void preOrderWithTracking(TreeNode root, List<String> result, LinkedList<TreeNode> tracking, List<LinkedList<TreeNode>> trackingResult) {
       
    
            if (root == null) {
       
                trackingResult.add(new LinkedList<>(tracking));
                return;
            }
    
            result.add(root.data);
    
            tracking.add(root.left);
            preOrderWithTracking(root.left, result, tracking, trackingResult);
            tracking.removeLast();
    
    
            tracking.add(root.right);
            preOrderWithTracking(root.right, result, tracking, trackingResult);
            tracking.removeLast();
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 输出结果
    ->A->B->D-> NULL
    ->A->B->D-> NULL
    ->A->B->E-> NULL
    ->A->B->E->F-> NULL
    ->A->B->E->F-> NULL
    ->A->C-> NULL
    ->A->C-> NULL
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    可以看出,之前我们遍历二叉树,以root==null作为判断条件时,所有的搜索路径,这里面对于叶子节点,会有2条重复的搜索结果,主要是由于分别遍历其左右子树,均为null,所有会出现2次搜索结果;

    3.二叉树的最大深度

    由上面二叉树的遍历,我们把遍历终止时的每一个遍历路径都打印了一遍,因此可以根据路径的size判断出二叉树的深度;

    4.多叉树的搜索

    • 文件目录的搜索
        /**
         * 递归遍历文件下的所有文件=>查找多叉树的叶子节点
         *
         * @param dir
         * @param allFile
         */
        public void listAllFile(File dir, List<File> allFile) {
       
            if (dir.isFile()) {
       
                allFile.add(dir);
                return;
            }
            File[] children = dir.listFiles();
            for (int i = 0; i < children.length; i++) {
       
                listAllFile(children[i], allFile);
            }
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 文件搜索时,搜索到叶子节点的所有路径
    public void listAllFilesWithTracking(File dir, LinkedList<File> tracking, List<LinkedList<File>> allTracking) {
       
            if (dir.isFile()) {
       
                //遍历到[文件],也就是叶子节点,返回,记录tracking路径
                allTracking.add(new LinkedList<>(tracking));
                return;
            }
            File[] children = dir.listFiles();
            for (int i = 0; i < children.length; i++) {
       
                tracking.add(children[i]);
                listAllFilesWithTracking(children[i], tracking, allTracking);
                tracking.removeLast();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    5.回溯算法的模板

    public void backtracking(选择列表,路径LinkedList tracking,所有路径resultTracking){
       
    	if(结束条件){
       
    		resultTracking.add(new LinkedList<>(tracking));//保存路径
    		return;
    	}
    	for (选择 in 选择列表){
       
    		tracking.add(选择)//做选择,将选择加入到选择列表
    		backtracking(选择列表,路径LinkedList tracking,所有路径List<LinkedList> resultTracking)
    		tracking.removeLast()//删除最后一个,撤销选择
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    从回溯算法的模板,再回看二叉树的遍历,其实相当于选择列表是二叉树的两个根节点[root.left,root.right],而且选择列表的具体引用是「变化」的;而回溯算法的「选择列表」一般是比较稳定的

    6.暴力破解密码问题

    小明无意中听到同坐的密码是有[1,2,3,4]4个数字组成,而且密码有6位数字,小明如何枚举所有的密码组成?有排列组合知识我们知道,总共有4的6次方种,那代码实现具体是什么呢?

    package com.mochuan.test.bt;
    
    import java.util.ArrayList;
    import java.util.LinkedList;
    import java.util.List;
    
    /**
     * 所有密码的组合
     */
    public class AllPassword {
       
    
        private int depth = 6;
    
        public static void main(String[] args) {
       
            new AllPassword().test();
        }
    
        public void test() {
       
            int[] word = {
       1, 2, 3, 4};
            LinkedList<Integer> tracking = new LinkedList<>();
            List<LinkedList<Integer>> allResult = new ArrayList<>();
            backtracking(word, tracking, allResult);
            System.out
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
  • 相关阅读:
    [HTML/CSS基础]知识点汇总
    除了ChatGPT,跨境电商必备的7个AI工具
    Spring Boot技术知识点:如何使用@Valid注解来对邮箱字段进行数据校验
    第5章 使用注解方式实现Spring Ioc
    Spring-MVC的crud增删改查--详细讲解
    这10张图拿去,别再说学不会RecyclerView的缓存复用机制了!
    移动WEB开发之流式布局--移动WEB开发之flex布局--携程网首页案例制作
    【HDFS】JN回滚大量edit日志导致Namenode主备切换的故障记录
    Rust机器学习之Polars
    @Slf4j注解的使用说明
  • 原文地址:https://blog.csdn.net/NUPTboyZHB/article/details/127909069