• 经典算法之回溯算法


    文章目录

    一、问题描述

    二、解题思路与代码

    三、回溯算法模板总结


    活动地址:21天学习挑战赛

    一、问题描述

    你有一套活字字模tiles,其中每个字模上都刻有一个字母tiles[i]。返回你可以印出的
    非空字母序列的数目。  注意:本题中,每个活字字模只能使用一次)
    示例1:
    输入:“AAB”
    输出:8
    解释:所有可能的序列为:“A”,“B”,“AA”,“AB”,“BA”,“AAB”,“ABA”,“BAA”
    示例2:
    输入:“AAABBC”
    输出:188
    提示:
    1.   1 < = t i l e s . l e n g t h < = 7
    2. t i l e s 大写英文字母组成


     

    二、解题思路与代码

    本题实际上是求输入的字符可以组成多少种不同的组合

     

     以上图为例来讲解一下本题的解题思路:

            这题中子集[A,B]和[B,A]被认为是两种不同的结果,所以每次都要从头开始选择,因为每个字符只能被使用一 次,所以如果使用之后下次就不能再使用了,这里可以使用一个数组visit来标记是否被使用过。
            但 这 里 有 个 难 点 就 是 怎 么 过 滤 掉 上 面 图 中 灰 色 的 部 分( 也 就 是 重 复 的 部 分) 举个例子,比如ABBCD,如果我们选择了第1个B,那么剩余的字符就变成了 ABCD ,这个时 候我们再选择第2个B是可以的。但如果我们没选择第1个B,直接选择第2个B,那么剩余的字符就是 ABCD ,和上面重复了。所以关键的去重代码大致是这样的:
    1. if (i - 1 >= 0 && chars[i] == chars[i - 1] && !used[i - 1])
    2. continue;

    解题代码(附测试类):

    1. package TwentyOne_Challenge;
    2. import java.util.Arrays;
    3. public class DayTen {
    4. public static void main(String[] args) {
    5. String tiles="AAB";
    6. System.out.println("AAB模板可以印出的非空字母序列的数目为:"+TilesSonNumber(tiles));
    7. System.out.println("AAABBC模板可以印出的非空字母序列的数目为:"+TilesSonNumber("AAABBC"));
    8. System.out.println("ABCDEF模板可以印出的非空字母序列的数目为:"+TilesSonNumber("ABCDEF"));
    9. }
    10. private static int TilesSonNumber(String tiles) {
    11. char[] chars = tiles.toCharArray();
    12. //先排序,目的是让相同的字符挨着,在下面计算的时候好过滤掉重复的
    13. Arrays.sort(chars);
    14. int[] res = new int[1];
    15. backtrack(res, chars, new boolean[tiles.length()], tiles.length(), 0);
    16. return res[0];
    17. }
    18. private static void backtrack(int[] res, char[] chars, boolean[] used, int length, int index) {
    19. //如果没有可以选择的就返回
    20. if (index == length)
    21. return;
    22. //注意,这里的i每次都是从0开始的,不是从index开始
    23. for (int i = 0; i < length; i++) {
    24. //一个字符只能选择一次,如果当前字符已经选择了,就不能再选了。
    25. if (used[i])
    26. continue;
    27. //过滤掉重复的结果
    28. if (i - 1 >= 0 && chars[i] == chars[i - 1] && !used[i - 1])
    29. continue;
    30. //选择当前字符,并把它标记为已选择
    31. used[i] = true;
    32. res[0]++;//选择一个字符,就多了一种结果
    33. //下一分支继续递归
    34. backtrack(res, chars, used, length, index + 1);
    35. //使用完之后再把它给复原。
    36. used[i] = false;
    37. }
    38. }
    39. }

     


     

    三、回溯算法模板总结

    1. private void backtrack("原始参数") {
    2. //终止条件(递归必须要有终止条件)
    3. if ("终止条件") {
    4. //一些逻辑操作(可有可无,视情况而定)
    5. return;
    6. }
    7. for (int i = "for循环开始的参数"; i < "for循环结束的参数"; i++) {
    8. //一些逻辑操作(可有可无,视情况而定)
    9. //做出选择
    10. //递归
    11. backtrack("新的参数");
    12. //一些逻辑操作(可有可无,视情况而定)
    13. //撤销选择
    14. }
    15. }

    回溯算法,通俗地解释就是不断的尝试,如果不成功,再继续回到上一步继续尝试直至成功,

    它的原理就是一个不断尝试的过程,这很励志。

  • 相关阅读:
    美团即时零售的优势不止“快”
    作为一个Java程序员,JVM的这些知识你懂了吗?
    飞桨模型转ONNX模型教程
    企业级监控方案——zabbix!(下)
    “date: write error: No space left on device”解决
    Vue3框架中CompositionAPI的基本使用(第十课)
    微软8月系统更新引发问题:虚拟内存分页文件出现错误
    xshell连接vmWare虚拟机(centos7)
    [Rust GUI]0.10.0版本iced代码示例 - progress_bar
    Docker + Selenium Grid 搭建分布式 UI 自动化测试
  • 原文地址:https://blog.csdn.net/qq_52487066/article/details/126436402