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

news2024/11/26 17:42:27

目录

    • 一、简介
      • 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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

NVRAM相关

1. Modem NVRAM四个分区 nvdata&#xff1a;手机运行过程中&#xff0c;使用(读写)的NVRAM(除了存在protect_f和protect_s中的NVRAM)都是该分区的nvram文件。存储着普通NVRAM数据、 IMEI、barcode、Calibration数据等。对应的modem path是Z:\NVRAM。NVRAM目录下有CALIBRAT、NVD…

用序列化思想为自动化测试「提供动力」

Python 对象序列化技术 对象序列化是指将对象从内存转换为字节流的过程&#xff0c;以实现对象的持久化存储和网络传输。它在许多场景中都非常重要&#xff0c;比如远程调用、长期数据存储等。 在Python中&#xff0c;我们主要使用pickle和marshal这两个模块来实现对象的序列…

【银行测试】第三方支付平台业务流,功能/性能/安全测试方法...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、第三方支付平台…

vue+echarts实现桑吉图的效果

前言&#xff1a; 在我们项目使用图形的情况下&#xff0c;桑吉图算是冷门的图形了&#xff0c;但是它可以实现我们对多级数据之间数据流向更好的展示的需求&#xff0c;比如&#xff0c;我们实际数据流向中&#xff0c;具有1对多&#xff0c;多对多的情况下&#xff0c;如果用…

2023年淘宝天猫年终惊喜红包玩法

2023年淘宝天猫年终惊喜红包玩法&#xff0c;2023年淘宝年终好价节红包活动 随着2023年的尾声渐近&#xff0c;淘宝再次为广大用户带来了年终的惊喜——一场特别的红包活动。从12月8日零时开始&#xff0c;直至12月12日的午夜&#xff0c;淘宝app将开启一个为期五天的年终好价节…

Web前端工程的装机必备软件

前言 最近作者的电脑 C 盘变红了&#xff0c;这让我很难受(有点小强迫症)&#xff0c;所以准备重新安装下系统&#xff0c;顺便把 C 盘扩大点。 注意&#xff1a; 操作系统是 windows 11 23H2。 所有的命令行都是使用 Windows Terminal 中进行的。 安装 Windows Terminal 由于…

L1-026:I Love GPLT

题目描述 这道超级简单的题目没有任何输入。 你只需要把这句很重要的话 —— “I Love GPLT”——竖着输出就可以了。 所谓“竖着输出”&#xff0c;是指每个字符占一行&#xff08;包括空格&#xff09;&#xff0c;即每行只能有1个字符和回车。 输入样例&#xff1a; 无输出样…

arm 交叉编译器版本下载

网址&#xff1a;https://releases.linaro.org/components/toolchain/binaries/ 根据板子架构选择正确的编译器 根据你主机架构选择对应的版本&#xff08;就是你的开发电脑架构&#xff09;

ROS小练习——服务调用

目录 一、服务名称与消息的获取 二、代码案例 1、C 2、python 三、编译运行 1、配置cmakelist 2、运行 一、服务名称与消息的获取 rosservice list rosservice type /spawn rossrv info turtlesim/Spawn 二、代码案例 1、C //包含头文件 #include "ros/ros.h&qu…

Linux AMH服务器管理面板本地安装与远程访问

最近&#xff0c;我发现了一个超级强大的人工智能学习网站。它以通俗易懂的方式呈现复杂的概念&#xff0c;而且内容风趣幽默。我觉得它对大家可能会有所帮助&#xff0c;所以我在此分享。点击这里跳转到网站。 文章目录 1. Linux 安装AMH 面板2. 本地访问AMH 面板3. Linux安装…

MySQL和MongoDB简介以及它们之间的区别

本文主要介绍MySQL和MongoDB的简介以及它们之间的区别。 目录 MySQL简介MySQL的优缺点MySQL的应用场景MongoDB简介MongoDB的优缺点MongoDB的应用场景MySQL和MongoDB的区别 MySQL简介 MySQL是一种开源的关系型数据库管理系统&#xff0c;是世界上最流行的数据库之一。它支持多用…

UVM:UVM的树形结构

UVM采用树形的组织结构来管理验证平台的各个部分。sequencer、driver、monitor、agent、model、 scoreboard、env等都是树的一个结点。为什么要用树的形式来组织呢&#xff1f;因为作为一个验证平台&#xff0c;它必须能够掌握自己治下的所 有“人口”&#xff0c;只有这样做了…

温度而知新,一文读懂Java知识文集精华版。

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

SL1581降压恒压 耐压4V-30V降压5V 2A电流 外围简单,四个元器件

SL1581是一款专为降压恒压应用而设计的芯片&#xff0c;具有耐压4V-30V、降压5V、2A电流输出等特点&#xff0c;外围电路简单&#xff0c;仅需四个元器件。 一、芯片介绍 SL1581是一款专为降压恒压应用而设计的芯片&#xff0c;它采用先进的PWM控制技术&#xff0c;具有高效率、…

Google 发布最强大模型Gemini,能力与GPT-4相当

一、Gemini 介绍 Gemini 是一个多模式模型系列&#xff0c;在图像、音频、视频和文本领域展示了真正强大的功能。其中功能最强大的模型 Gemini Ultra 在 32 个基准测试中的 30 个中提升了最先进水平&#xff0c;其中包括 12 个流行文本和推理基准测试中的 10 个、9 个图像理解…

情感分析简化:使用python中的TextBlob库轻松分析文本情感

导语&#xff1a;在当今社交媒体和大数据时代&#xff0c;了解人们对于产品、服务或事件的情感倾向变得至关重要。TextBlob库作为一个简单易用的自然语言处理工具&#xff0c;提供了强大的情感分析功能&#xff0c;帮助我们更深入地理解文本背后的情感。本文将介绍TextBlob库的…

Python3+RIDE+RobotFramework自动化测试框架搭建过程详解

一、Python安装 最新版Python下载地址&#xff1a;https://www.python.org/ 根据操作系统选择对应版本制品下载安装即可&#xff0c;本机用的是Windows x86-64 executable installer。 注意事项&#xff1a; 安装完成后检查下环境变量&#xff0c;默认会配置好&#xff0c;可…

HarmonyOS开发(十):通知和提醒

1、通知概述 1.1、简介 应用可以通过通知接口发送通知消息&#xff0c;终端用户可以通过通知栏查看通知内容&#xff0c;也可以点击通知来打开应用。 通知使用的的常见场景&#xff1a; 显示接收到的短消息、即使消息...显示应用推送消息显示当前正在进行的事件&#xff0c…

2024黑龙江省职业院校技能大赛信息安全管理与评估赛项规程

2024黑龙江省职业院校技能大赛暨国赛选拔赛 “GZ032信息安全管理与评估”赛项规程 极安云科专注技能竞赛&#xff0c;包含网络建设与运维和信息安全管理与评估两大赛项&#xff0c;及各大CTF&#xff0c;基于两大赛项提供全面的系统性培训&#xff0c;拥有完整的培训体系。团队…

Python实现广义线性回归模型(statsmodels GLM算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 广义线性模型(Generalized Linear Model&#xff0c;简称GLM)是一种广泛应用于回归分析和分类问题的统…