有趣的BackTracking回溯算法

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

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;
    }
}

构建的二叉树,图示详见: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();

    }
  • 输出结果
->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

可以看出,之前我们遍历二叉树,以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);
        }
    }

  • 文件搜索时,搜索到叶子节点的所有路径
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();
        }
    }

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()//删除最后一个,撤销选择
	}
}

从回溯算法的模板,再回看二叉树的遍历,其实相当于选择列表是二叉树的两个根节点[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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值