数据结构与算法(五)回溯算法(Java)

目录

    • 一、简介
      • 1.1 定义
      • 1.2 特性
      • 1.3 结点知识补充
      • 1.4 剪枝函数
      • 1.5 使用场景
      • 1.6 解空间
      • 1.7 实现模板
    • 二、经典示例
      • 2.1 0-1 背包问题
      • 2.2 N皇后问题

一、简介

1.1 定义

回溯法(back tracking)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回到上一步重新选择,这种走不通就退回再走的技术为 回溯法,而满足回溯条件时某个状态的点称为 “回溯点”

1.2 特性

回溯法是一个既带有 系统性 又带有 跳跃性 的搜索算法。

  • 系统性: 它在包含问题的所有解的解空间树中,按照 深度优先的策略,从根节点出发搜索解空间树。
  • 跳跃性: 算法搜索至解空间树的任一结点时,判断该结点为根的子树是否包含问题的解,如果肯定不包含,则跳过以该结点为根的子树的搜索,逐层向其始祖结点回溯。否则,进入该子树,继续深度优先的策略进行搜索。

这种以深度优先的方式系统地搜索问题解的算法称为回溯法,它 适用于解一些组合数较大的问题

1.3 结点知识补充

  • 扩展结点: 一个 正在生成儿子 的结点,称为扩展结点。
  • 活结点: 一个 自身已生成但其儿子还没有全部生成 的结点,称为活结点。
  • 死结点: 一个 所有儿子已经全部生成 的结点,称为死结点。

深度优先策略:

  • 如果对一个扩展结点 R,一旦生成了它的一个儿子 C,就把 C 当作新的扩展结点。
  • 在完成对子树 C(以 C 为根的子树)的穷尽搜索之后,将 R 重新变成扩展结点,继续生成 R 的下一个儿子(如果存在)。

广度优先策略:

  • 在一个扩展结点变成死结点之前,它一直是扩展结点。

回溯法:

  • 为了避免生成那些不可能产生最佳解的问题状态,要不断地利用 限界函数(bounding function)来处死那些实际上不可能产生所需解的活结点,以减少问题的计算量。
  • 具有剪枝函数的深度优先生成法称为回溯法。

那什么是限界函数呢?限界函数是剪枝函数的一种,下面我们一起来看下剪枝函数。

1.4 剪枝函数

剪枝函数:当某个顶点没有希望,则其所在的树枝可以减去。

剪枝函数一般有两种:

  • 约束函数: 剪去不满足约束条件的路径。
  • 限界函数: 减去不能得到最优解的路径。

1.5 使用场景

回溯就是递归的副产品,只要有递归就会有回溯。 其实回溯就是 递归搜索+剪枝,并不是什么高效的算法。

回溯算法的应用:

  • 当问题是要满足某种性质(约束条件)的所有接或最优解时,往往使用回溯法。

1.6 解空间

当确定回溯后,问题的关键转化为 如何定义问题的解空间,且转化为树结构,可以称之为 解空间树

解空间树分为:

  • 子集树: 当所给的问题是 从 n 个元素的集合中找到某种性质的子集 时,相应的解空间变为子集树。如:0-1 背包问题,从所给重量、价值不同的物品中挑选几个物品放入背包,使得在满足背包不超重的情况下,背包内物品价值最大。
  • 排列数: 所给的问题是确定 n 个元素满足某种性质的排列时,相应的解空间就是排列树。如:旅行问题,一个人把几个城市旅游一遍,要求走的路程最小,它的解法就是几个城市的排列。

1.7 实现模板

// 一定要分成横纵两个方面思考问题
public void backTracking(参数) {
    if (终止条件) {
        // 存放结果
        return;
    }
    
    // 注意 i=0,i=start 的区别
    for (选择:本层集合中元素(树中结点孩子的数量就是集合的大小)) {
        // 处理节点
        backTracking(路径,选择列表); // 递归,注意 i 和 i++ 的区别
        // 回溯,撤销处理结果
    }
}

二、经典示例

2.1 0-1 背包问题

题目:

有一个背包,容量由你自己输入,有n个物品,每个物品都具有容量与价值,这些都是由你自己输入的,请问,要怎么放物品到背包里,才能使得总价值最大呢,放入背包的总容量要小于等于背包的总容量。(如果一个物品放不下,则可以拆分成多个小块)
背包:M:100
物品:N:7
重量 价值
10 20
20 40
30 30
25 20
50 40
10 35
60 70

思路:

  • 迭代进行深度优先遍历;
  • 如果重量超出容量不予考虑;
  • 不超出容量情况下,获取价值最大值。

代码实现:

public static void main(String[] args) {
    int[][] items = new int[7][2];
    // 重量 价值
    items[0][0] = 10; items[0][1] = 20;
    items[1][0] = 20; items[1][1] = 40;
    items[2][0] = 30; items[2][1] = 30;
    items[3][0] = 25; items[3][1] = 20;
    items[4][0] = 50; items[4][1] = 40;
    items[5][0] = 10; items[5][1] = 35;
    items[6][0] = 60; items[6][1] = 70;
    int capacity = 100;
    System.out.println("背包的容量:" + capacity);
    StringBuilder builder = new StringBuilder();
    for (int[] item : items) {
        builder.append(Arrays.toString(item));
    }
    System.out.println(items.length + " 个物品的重量、价值:" + builder.toString());
    int maxValue = maxValue(items, capacity);
    System.out.println("最大价值:" + maxValue);
}

private static int maxValue(int[][] items, int capacity) {
    // 0-未放入背包 1-放入背包
    int[] flag = new int[items.length];
    List<int[]> record = new ArrayList<>();
    int maxValue = maxValue(items, capacity, flag, 0, 0, record);
    for (int[] item : record) {
        capacity = capacity - item[0];
        System.out.println("重量为 " + item[0] + ",价值为 " + item[1] + " 的物品被放入背包,剩余容量:" + capacity);
    }
    return maxValue;
}

private static int maxValue(int[][] items, int capacity, int[] flag, int weightSum, int oldMaxValue, List<int[]> record) {
    if (weightSum > capacity) {
        return -1;
    }

    int maxValue = oldMaxValue;
    for (int i = 0; i < items.length; i++) {
        if (flag[i] == 0) {
            flag[i] = 1;
            int tmpValue = maxValue(items, capacity, flag, weightSum + items[i][0], oldMaxValue + items[i][1], record);
            if (tmpValue != -1) {
                if (tmpValue > maxValue) {
                    maxValue = tmpValue;
                    record.clear();
                    // 用于记录日志
                    for (int j = 0; j < flag.length; j++) {
                        if (flag[j] == 1) {
                            record.add(items[j]);
                        }
                    }
                }
            }
            flag[i] = 0;
        }
    }
    return maxValue;
}

执行结果:

在这里插入图片描述

2.2 N皇后问题

N皇后

题目:

设计一种算法,打印 N 皇后在 N × N 棋盘上的各种摆法,其中每个皇后都不同行、不同列,也不在对角线上。这里的“对角线”指的是所有的对角线,不只是平分整个棋盘的那两条对角线。

注意:本题相对原题做了扩展

示例:

 输入:4
 输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
 解释: 4 皇后问题存在如下两个不同的解法。
[
 [".Q..",  // 解法 1
  "...Q",
  "Q...",
  "..Q."],

 ["..Q.",  // 解法 2
  "Q...",
  "...Q",
  ".Q.."]
]

代码实现:

public static void main(String[] args) {
    List<List<String>> result = solveNQueens(4);
    result.forEach(o -> {
        o.forEach(System.out::println);
        System.out.println("----");
    });
}

public static List<List<String>> solveNQueens(int n) {
    List<List<String>> list = new ArrayList<>();
    handleQueens(n, 0, new int[n], new ArrayList<>(), new HashSet<>(), new HashSet<>(), list);
    return list;
}

private static void handleQueens(int n, int level, int[] flag, List<String> queens, Set<Integer> diagonalSet, Set<Integer> reverseDiagonalSet, List<List<String>> list) {
    if (level >= n) {
        list.add(new ArrayList<>(queens));
        return;
    }

    for (int i = 0; i < n; i++) {
        int diagonal = i - level;
        int reverseDiagonal = i + level;
        if (flag[i] == 0 && !diagonalSet.contains(diagonal) && !reverseDiagonalSet.contains(reverseDiagonal)) {
            flag[i] = 1;
            queens.add(getLineStr(i, n));
            diagonalSet.add(diagonal);
            reverseDiagonalSet.add(reverseDiagonal);
            handleQueens(n, level + 1, flag, queens, diagonalSet, reverseDiagonalSet, list);
            reverseDiagonalSet.remove(reverseDiagonal);
            diagonalSet.remove(diagonal);
            queens.remove(queens.size() - 1);
            flag[i] = 0;
        }
    }
}

private static String getLineStr(int i, int n) {
    StringBuilder builder = new StringBuilder();
    for (int j = 0; j < n; j++) {
        builder.append(j == i ? "Q" : ".");
    }
    return builder.toString();
}

执行结果:

在这里插入图片描述

整理完毕,完结撒花~ 🌻





参考地址:

1.回溯算法详细总结,https://zhuanlan.zhihu.com/p/165083789

2.回溯算法(BackTracing),https://zhuanlan.zhihu.com/p/495574746?utm_id=0

3.彻底搞懂回溯法(本文真的很详细),https://blog.csdn.net/m0_52824954/article/details/123467217

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/225586.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

为什么 AWS 数据库不讲 HTAP

在 AWS re:Invent 2023 掌门人 Adam Selipsky 的 Keynote 上&#xff0c;数据库方面最重磅的主题是 Zero-ETL&#xff0c;从 TP 数据库 (RDS, Aurora, DynamoDB) 同步数据到 AP 数据库 (Redshift)。 Zero-ETL 是 AWS 在去年 re:invent 2022 上推出的概念&#xff0c;今年则继…

网络层之IP组播

学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。各位小伙伴&#xff0c;如果您&#xff1a; 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持&#xff0c;想组团高效学习… 想写博客但无从下手&#xff0c;急需…

【力扣】206.反转链表

206.反转链表 这道题有两种解法&#xff0c;但不只有两种&#xff0c;嘿嘿。 法一&#xff1a;迭代法 就是按循序遍历将每一个指针的指向都给改了。比如说1——>2——>3改为null<——1<——2<——3这样。那这里以第二个结点为例&#xff0c;想一想。我想要指向…

如何在Linux上部署1Panel运维管理面板并远程访问内网Web端管理界面

文章目录 前言1. Linux 安装1Panel2. 安装cpolar内网穿透3. 配置1Panel公网访问地址4. 公网远程访问1Panel管理界面5. 固定1Panel公网地址 前言 1Panel 是一个现代化、开源的 Linux 服务器运维管理面板。高效管理,通过 Web 端轻松管理 Linux 服务器&#xff0c;包括主机监控、…

BPM、ERP、OA 各自的功能和特点是什么?怎么配合使用?

OA、BPM、ERP几乎是任何一家企业都会接触到的信息管理系统及程序。 首先&#xff0c;我从定义上理清BPM、ERP和OA ERP(Enterprise Resource Planning,企业资源计划)&#xff0c;一般围绕供应链、生产制造和财务为核心。 BPM&#xff08;business process management&#xf…

智能优化算法应用:基于野马算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于野马算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于野马算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.野马算法4.实验参数设定5.算法结果6.参考文献7.MATLAB…

如何使用Net2FTP轻松部署本地Web文件管理器并远程访问管理内网资源?

文章目录 1.前言2. Net2FTP网站搭建2.1. Net2FTP下载和安装2.2. Net2FTP网页测试 3. cpolar内网穿透3.1.Cpolar云端设置3.2.Cpolar本地设置 4.公网访问测试5.结语 1.前言 文件传输可以说是互联网最主要的应用之一&#xff0c;特别是智能设备的大面积使用&#xff0c;无论是个人…

Cocos Creator加入图片没有被识别

原因&#xff0c;需要更换类型&#xff0c;选择下图中的类型

响应式编程又变天了?看JDK21虚拟线程如何颠覆!

本文解释为啥会有响应式编程&#xff0c;为什么它在开发者中不太受欢迎&#xff0c;以及引入 Java 虚拟线程后它可能最终会消失。 命令式风格编程一直深受开发者喜爱&#xff0c;如 if-then-else、while 循环、函数和代码块等结构使代码易理解、调试&#xff0c;异常易追踪。然…

Word 在页眉或页脚中设置背景颜色

目录预览 一、问题描述二、解决方案三、参考链接 一、问题描述 如何在word的页眉页脚中设置背景色&#xff1f; 二、解决方案 打开 Word 文档并进入页眉或页脚视图。在 Word 2016 及更高版本中&#xff0c;你可以通过在“插入”选项卡中单击“页眉”或“页脚”按钮来进入或者…

移动云“遇见大咖”|玻色量子副总裁巨江伟:超越摩尔定律的新型计算革命

移动云MVP&#xff0c;作为产品共建专家、关键意见领袖及技术布道者&#xff0c;帮助开发者更好地了解和使用移动云。开发者社区希望携手移动云MVP&#xff0c;与开发者共生、共赢、共成长。 8月31日&#xff0c;移动云开发者社区“遇见大咖”系列活动第2期——“[量子计算]超越…

用的云服务器一直被ddos攻击怎么解决

德迅云安全在给客户提供网络安全解决方案的时候&#xff0c;经常会有遇到不少客户&#xff0c;是再用的云服务器被攻击了&#xff0c;来询问德迅云有什么方案来解决&#xff1f;其实目前的攻击主要都是DDOS攻击了&#xff0c;服务器的攻击方式&#xff0c;常见的也就是这几种了…

Apache Hive(部署+SQL+FineBI构建展示)

Hive架构 Hive部署 VMware虚拟机部署 一、在node1节点安装mysql数据库 二、配置Hadoop 三、下载 解压Hive 四、提供mysql Driver驱动 五、配置Hive 六、初始化元数据库 七、启动Hive(Hadoop用户) chown -R hadoop:hadoop apache-hive-3.1.3-bin hive 阿里云部…

彻底解决module java.base does not “opens java.io“

需求背景 最近在使用android studio导入hbuilder的HBuilder-Integrate-AS工程时候报错&#xff0c;错误消息如下两种。 错误描述 第一种 Failed to notify dependency resolution listener. void org.gradle.api.artifacts.DependencySubstitutions$Substitution.with(org.g…

IDEA中配置Git

Git 在IDEA中使用Git1 在IDEA中配置Git2 在IDEA中使用Git2.1在IDEA中创建工程并将工程添加至Git2.2 将文件添加到暂存区2.3 提交文件2.4 将代码推送到远程仓库2.5 从远程仓库克隆工程到本地2.6 从远程拉取代码2.7 版本对比2.8 创建分支2.9 切换分支2.10 分支合并 3 使用IDEA进行…

跨境电商平台投资智谋:全球化布局的策略之道

随着全球数字化浪潮的涌动&#xff0c;跨境电商平台在全球商业舞台上扮演着越来越重要的角色。其全球化布局的策略之道成为业界瞩目的焦点。 本文将深入探讨跨境电商平台投资的智谋&#xff0c;分析其全球化布局的关键策略&#xff0c;以及在这个竞争激烈的领域中脱颖而出的成…

数据库数据恢复—sqlserver数据库文件被加密,文件名被篡改的数据恢复案例

SQLServer数据库故障&#xff1a; 某公司服务器上的SQLServer数据库被加密&#xff0c;无法使用。被加密的数据库有2个&#xff0c;数据库的MDF、LDF、log文件名字被篡改。 数据库被加密截图&#xff1a; 数据库备份被加密&#xff0c;文件名字被篡改&#xff1a; SQLServer数…

在JSP项目中编写一个接口返回JSON 供JSP界面异步请求数据

首先 我们要引入json处理的依赖工具 在 pom.xml文件的 dependency 标签中加入如下代码 <dependency><groupId>com.googlecode.json-simple</groupId><artifactId>json-simple</artifactId><version>1.1.1</version> </dependenc…

【java】Java程序员,你掌握了多线程吗?

摘要&#xff1a;互联网的每一个角落&#xff0c;无论是大型电商平台的秒杀活动&#xff0c;社交平台的实时消息推送&#xff0c;还是在线视频平台的流量洪峰&#xff0c;背后都离不开多线程技术的支持。在数字化转型的过程中&#xff0c;高并发、高性能是衡量系统性能的核心指…

阿里云效部署前后端

静态站点到OSS 阿里云-云效&#xff0c;阿里云企业级一站式 DevOps&#xff0c;可以免费使用&#xff08;会限制人数、流水线数量等&#xff0c;个人项目够用了&#xff09;。相关文章 CI 持续集成 - 阿里云云效 OSS 是对象存储的意思&#xff0c;一般一个项目对应一个 Bucke…