回溯算法—组合问题

文章目录

  • 介绍
  • 应用问题
  • 基本流程
  • 算法模版
  • 例题
    • (1)组合
    • (2)电话号码的字母组合

介绍

回溯算法实际上是 一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。

应用问题

  • 组合问题:N个数里面按一定规则找出k个数的集合
  • 子集问题:一个N个数的集合里有多少符合条件的子集
  • 切割问题:一个字符串按一定规则有几种切割方式
  • 排列问题:N个数按一定规则全排列,有几种排列方式
  • 棋盘问题:N皇后,解数独等等

基本流程

  • 选择:在当前步骤中,从可选的选择中选择一个。
  • 验证:检查选择是否满足问题的约束条件和限制。
  • 递归:进入下一步骤,继续选择和验证。
  • 撤销:如果选择不满足问题要求,回溯到上一步,撤销当前选择,并尝试其他选择。

回溯法解决的问题都可以抽象为树形结构

集合的大小就构成了树的宽度,递归的深度就构成了树的深度

算法模版

void backtracking(参数) {if (终止条件) {存放结果;return;}for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {处理节点;backtracking(路径,选择列表); // 递归回溯,撤销处理结果}
}

递归嵌套for循环

回溯三部曲:

  1. 递归函数的参数和返回值:返回值一般为void
  2. 确定终止条件
  3. 单层搜索的逻辑

例题

(1)组合

77. 组合 - 力扣(LeetCode)

给定两个整数 nk,返回范围 [1, n] 中所有可能的 k 个数的组合。

你可以按 任何顺序 返回答案。

示例 1:

输入:n = 4, k = 2
输出:
[[2,4],[3,4],[2,3],[1,2],[1,3],[1,4],
]

组合是无序的,[1,2]和[2,1]一致

每一个节点都是一层for循环,从startIndex开始

tree
  1. 递归函数的参数和返回值:

    void backTracking(int n, int k, int startIndex)
    
  2. 确定终止条件

    到达叶子节点,path数组大小到达k

    if (pathtop == k) 
    
  3. 单层搜索的逻辑

    for循环从startIndexn中选择数字(startIndex控制每次搜索时的起始位置,本题第一次调用时传1

    先处理当前i节点;
    再递归:传入i+1控制下一层递归起始位置;
    回溯:退出递归调用之后,需要回溯到之前的状态,来尝试其他数字并构建其他组合。因此 pathtop 减 1,i + 1退出递归后在当前循环还是i

    for (int i = startIndex; i <= n; i++) {path[pathtop++] = i; // 存入结果backTracking(n, k, i + 1); // 递归,传入i+1,下一层递归起始位置pathtop--;
    }
    

    for循环横向遍历,递归纵向遍历,回溯不断调整结果集

int* path;
int pathtop;
int** ret;
int rettop;
void backTracking(int n, int k, int startIndex) {if (pathtop == k) {int* temp = (int*)malloc(sizeof(int) * k);for (int i = 0; i < k; i++) {temp[i] = path[i];}ret[rettop++] = temp;return;}for (int i = startIndex; i <= n; i++) {path[pathtop++] = i;backTracking(n, k, i + 1);pathtop--;}
}
int** combine(int n, int k, int* returnSize, int** returnColumnSizes) {pathtop = 0;rettop = 0;path = (int*)malloc(sizeof(int) * k);ret = (int**)malloc(sizeof(int*) * 1000000);backTracking(n, k, 1);*returnSize = rettop;(*returnColumnSizes) = (int*)malloc(sizeof(int) * (*returnSize));for (int i = 0; i < (*returnSize); i++) {(*returnColumnSizes)[i] = k;}return ret;
}

剪枝

时间复杂度:叶子个数乘叶子到根的路径长度

for循环在寻找起点的时候要有一个范围,如果这个起点到集合终止之间的元素已经不够题目要求的k个元素了,就没有必要搜索了

例如如果n = 4, k = 4情况下

nk4

已经选择的数量:pathTop

还需要选择的数量:k - pathTop

集合n中至多要从n - (k - pathTop) + 1 开始遍历

[1,2,3,4]

n=4,k=3,假设选了一个了,还要选两个,那么至多从n - 2 + 1 = 3

假设选了0个,那么还要选三个,那么至多从n - 3 + 1 = 2

 for (int i = startIndex; i <= (n - k + pathtop + 1); i++) {path[pathtop++] = i;backTracking(n, k, i + 1);pathtop--;}

(2)电话号码的字母组合

17. 电话号码的字母组合 - 力扣(LeetCode)

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

示例 1:

输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]

示例 2:

输入:digits = ""
输出:[]
  1. 递归函数的参数和返回值:

    void backTracking(char* digits, int length, int index)
    
  2. 确定终止条件

    遍历到字符串末尾

     if (pathTop == length) 
    
  3. 单层搜索的逻辑

    创建一个map[10][5] 来存储不同号码对应的字母

    for循环遍历的是map[i] 的长度
    在这里插入图片描述

char** ret;
int retTop;
char* path;
int pathTop;
char map[10][5] = {"\0",    "\0",    "abc\0",  "def\0", "ghi\0","jkl\0", "mno\0", "pqrs\0", "tuv\0", "wxyz\0"};
void backTracking(char* digits, int length, int index) {// 当遍历到字符串末尾时,将当前组合加入结果集中if (index == length) {char* temp = (char*)malloc(sizeof(char) * (pathTop + 1));memcpy(temp, path, sizeof(char) * pathTop);temp[pathTop] = '\0';ret[retTop++] = temp; return;}// 获取当前数字对应的字符集合的长度int num = digits[index] - '0';int letterLength = strlen(map[num]);// 遍历当前数字对应的字符集合for (int i = 0; i < letterLength; i++) {// 当前组合长度达到字符串长度时,退出循环if (pathTop >= length) {break;}// 将当前字符加入当前组合中path[pathTop++] = map[num][i];backTracking(digits, length, index + 1);pathTop--;}
}// 主函数,生成数字键盘对应的所有字母组合
char** letterCombinations(char* digits, int* returnSize) {retTop = pathTop = 0;int maxPathLength = strlen(digits) * 4; path = (char*)malloc(sizeof(char) * (maxPathLength + 1));ret = (char**)malloc(sizeof(char*) * 1000);// 初始化结果集数组为空for (int i = 0; i < 1000; ++i) {ret[i] = NULL;}int length = strlen(digits);// 输入字符串长度为0返回空结果集if (length == 0) {*returnSize = 0;return ret;}backTracking(digits, length, 0);*returnSize = retTop; return ret; 
}

参考:

  1. 代码随想录 (programmercarl.com)
  2. 回溯算法套路②组合型回溯+剪枝
  3. 17. 电话号码的字母组合 - 力扣(LeetCode)


如有错误烦请指正。

感谢您的阅读

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring添加注解读取和存储对象

5大注解 Controller 控制器 Service 服务 Repository 仓库 Componet 组件 Configuration 配置 五大类注解的使用 //他们都是放在同一个目录下&#xff0c;不同的类中 只不过这里粘贴到一起//控制器 Controller public class UserController {public void SayHello(){System.ou…

C++进阶 | [3] 搜索二叉树

摘要&#xff1a;什么是搜索二叉树&#xff0c;实现搜索二叉树&#xff08;及递归版本&#xff09; 什么是搜索二叉树 搜索二叉树/二叉排序树/二叉查找树BST&#xff08;Binary Search Tree&#xff09;&#xff1a;特征——左小右大&#xff08;不允许重复值&#xff09;。即…

pydev debugger: process **** is connecting

目录 解决方案一解决方案二 1、调试时出现pydev debugger: process **** is connecting 解决方案一 File->settings->build,execution,deployment->python debugger 下面的attach to subprocess automatically while debugging取消前面的勾选&#xff08;默认状态为勾…

rbac权限和多级请假设计的流程演示和前端页面实现

登录账号&#xff1a;t6普通用户 t7部门经理 m8总经理 密码都为&#xff1a;test 多级请假&#xff1a;7级及以下申请请假需要部门经理审核&#xff0c;若是请假时长超过72小时&#xff0c;则需要总经理审核&#xff0c;7级申请请将需要总经理审核&#xff0c;总经理请假自动审…

Go 语言基础之常用包【flag、time、strconv、io】

1、命令行参数包 flag flag 包就是一个用来解析命令行参数的工具。 1.1、os.Args import ("fmt""os" )func main() {if len(os.Args) > 0 {for index, arg : range os.Args {fmt.Printf("args[%d]%v\n", index, arg)}} } 运行结果&#…

Relaxed MemoryConsistency

SC和TSO都被称之为强&#xff08;strong&#xff09;保序模型&#xff1b; because the global memory order of each model usually respects (preserves) per-thread program order&#xff1b;回想一下&#xff0c;对于load和store的所有四种组合&#xff08;Load -> Lo…

【系统分析师】软件架构设计

文章目录 1、构件与软件复用1.1 主流构件标准1.2 构件获取与管理1.3 构件复用的方法 2、软件架构概述3、软件架构建模4、软件架构风格4.1 经典架构风格4.2 层次架构风格4.3 富互联网应用-RIA 5、面向服务的架构5.1 SOA概述5.2 SOA的关键技术5.3 SOA的实现方法 6、软件架构评估6…

文心一言 VS 讯飞星火 VS chatgpt (256)-- 算法导论18.3 2题

二、请写出 B-TREE-DELETE 的伪代码。如果要写代码&#xff0c;请用go语言。 文心一言&#xff1a; 首先&#xff0c;我会提供 B-Tree 删除操作的伪代码&#xff0c;然后我会给出使用 Go 语言实现的简化版本。请注意&#xff0c;一个完整的 B-Tree 实现会涉及很多细节和边界情…

ArcGIS10.2系列许可到期解决方案

本文手机码字&#xff0c;不排版了。 昨晚&#xff08;2021\12\17&#xff09;12点后&#xff0c;收到很多学员反馈 ArcGIS10.2系列软件突然崩溃。更有的&#xff0c;今天全单位崩溃。 ​ 提示许可15天内到期。之前大部分许可是到2021年1月1日的。 ​ 后续的版本许可都是永久的…

Leetcode—2105. 给植物浇水 II【中等】

2024每日刷题&#xff08;131&#xff09; Leetcode—2105. 给植物浇水 II 实现代码 class Solution { public:int minimumRefill(vector<int>& plants, int capacityA, int capacityB) {int size plants.size();int i 0;int j size - 1;int capA capacityA;in…

java spring 10 Bean的销毁过程 上 在docreatebean中登记要销毁的bean

1.Bean销毁是发送在Spring容器关闭过程中的 AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(AppConfig.class);UserService userService (UserService) context.getBean("userService");userService.test();// 容器关闭cont…

如何自定义Markdown中插入图片的位置

工作中常常需要在VsCode下写Markdown笔记&#xff0c;在写笔记的过程中不免需要插入图片。  Markdown中插入笔记的操作往往是比较繁琐的&#xff0c;比如&#xff1a;在文档中引用本地某个文件夹下的图片&#xff0c;首先需要你先保存图片到本地路径&#xff0c;然后需要你在文…