当前位置: 首页 > news >正文

算法——回溯

学习目标:

  • 掌握算法入门知识

学习内容:

  1. 回溯的定义
  2. 例题详细步骤讲解(求子集、求全排列)

1. 回溯的定义

回溯法是一种通过 试探性搜索 来求解问题的算法思想。一个复杂问题的解决方案是由若干个小的决策步骤组成的决策序列,解决一个问题的所有可能的决策序列构成该问题的解空间。解空间中满足约束条件的决策序列称为可行解。在约束条件下使目标达到最优的可行解称为该问题的最优解。其核心思想是:
(1)逐步构建候选解,并在每一步检查是否满足问题的约束条件。
(2)如果当前路径 不可能得到有效解,则立即 回退(回溯),尝试其他可能的路径。
(3)通过 递归 或 栈(迭代) 实现状态的深入和回退。

回溯法的特点:
(1)系统性:按某种顺序(如 DFS)遍历所有可能的解。
(2)避免无效搜索:通过剪枝(Pruning)提前终止不可能的分支。
适用于组合问题:如排列、组合、子集、棋盘类问题(N 皇后、数独)等。

2. 例题详细步骤讲解

2.1 子集树——求子集

例1:a[]={1,2,3},所有子集是:{},{3},{2},{2,3},{1},{1,3},{1,2},{1,2,3}(输出顺序无关)。

思路:解向量为x[],x[i]=0表示不选择a[i],x[i]=1表示选择a[i]。用 i 扫描数组 a ,也就是说问题的初始状态为(i=0,x的元素均为0),目标状态为(i=n,x为一个解,进行输出)。

  • 不选择a[i]元素,x[i]=0 ==> 下一个状态(递归)转向(i+1)。
  • 选择a[i]元素,x[i]=1 ==> 下一个状态(递归)转向(i+1)。

在这里插入图片描述
代码:

#include <stdio.h>

void dfs(int a[], int n, int i, int x[]) {
    if (i >= n) {  //拿到一个解向量,进行输出
        for (int j = 0; j < n; j++) {
            if (x[j] != 0) {
                printf("%d ", a[j]); // 打印选中的元素
            }
        }
        printf("\n");
    } else {
        x[i] = 0; // 不选择a[i]
        dfs(a, n, i + 1, x);
        
        x[i] = 1; // 选择a[i]
        dfs(a, n, i + 1, x);
    }
}

int main() {
    int a[] = {1, 2, 3}; // 示例数组
    int n = sizeof(a) / sizeof(a[0]); // 计算数组长度
    int x[n]; // 辅助数组,用于记录是否选择对应元素
    
    printf("数组的所有子集:\n");
    dfs(a, n, 0, x); // 从第0个元素开始回溯
    
    return 0;
}
2.2 排列树——求全排列

例2:有一个含 n 个整数的数组 a ,所有元素均不相同,求其所有元素的全排列。 如:a[]={1,2,3},得到结果是(1,2,3)、(1,3,2)、(2,3,1)、(2,1,3)、(3,1,2)、(3,2,1)。

思路:以1开头的两组后面两元素正好是交换位置的(1,2,3)、(1,3,2),以2、3开头的两组后面两元素正好也是交换位置。说明涉及一个元素交换。再看每两组刚好是1、2、3开头,也可以设置成交换位置得到,如1和1交换,2和1交换,3和1交换。也就是说,整个代码应该围绕交换进行。

DFS(a,n,i)  ==>  输出a所有元素即产生一种全排列         i=n-1
DFS(a,n,i)   ==> for (j=i;j<n;j++)		            其他情况i<n-1
                     { 交换a[i]与a[j];  DFS(a,n,i+1); 交换a[i]与a[j]; }

初始状态为(1,2,3),DFS(a,n,0)
(1)i=0,i<n-1,进行for循环,j=i=0,交换a[0]和a[0],得到(1,2,3),递归进入下一层DFS(a,n,1)
(2)i=1,i<n-1,进入for循环,j=i=1,交换a[1]和a[1],得到(1,2,3),递归进入下一层DFS(a,n,2)
(3)i=2,i=n-1,输出一种排列(1,2,3)
(4)回到(2),交换a[1]和a[1],得到(1,2,3)。j++=2,交换a[1]和a[2],得到(1,3,2),递归进入下一层DFS(a,n,2)
(5)i=2,i=n-1,输出一种排列(1,3,2)
(6)回到(2),交换a[1]和a[2],得到(1,2,3)。j++=3,循环结束,回到(1),交换a[0]和a[0],得到(1,2,3)。j++=1,交换a[0]和a[1],得到(2,1,3),递归进入下一层DFS(a,n,1)
(7)i=1,i<n-1,进入for循环,j=i=1,交换a[1]和a[1],得到(2,1,3),递归进入下一层DFS(a,n,2)
(8)i=2,i=n-1,输出一种排列(2,1,3)
(9)回到(7),交换a[1]和a[1],得到(2,1,3)。j++=2,交换a[1]和a[2],得到(2,3,1),递归进入下一层DFS(a,n,2)
(10)i=2,i=n-1,输出一种排列(2,3,1)
(11)回到(7),交换a[1]和a[2],得到(2,1,3)。j++=3,循环结束,回到(1),交换a[0]和a[1],得到(1,2,3)。j++=2,交换a[0]和a[2],得到(3,2,1),递归进入下一层DFS(a,n,1)
(12)第三个大分支省略… 应该会了吧!!!

在这里插入图片描述

代码:

#include <stdio.h>

// 交换两个整数的值
void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

// 回溯法生成全排列
void DFS(int a[], int n, int i) {
    if (i >= n - 1) {  // 递归出口:已处理完所有元素
        for (int k = 0; k < n; k++) {
            printf("%d ", a[k]);  // 打印当前排列
        }
        printf("\n");
    } else {
        for (int j = i; j < n; j++) {
            swap(&a[i], &a[j]);   // 交换a[i]和a[j]
            DFS(a, n, i + 1);     // 递归处理下一个位置
            swap(&a[i], &a[j]);   // 恢复交换(回溯)
        }
    }
}

int main() {
    int a[] = {1, 2, 3};  // 示例数组
    int n = 3;  // 数组长度

    printf("数组的全排列:\n");
    DFS(a, n, 0);  // 从第0个位置开始生成全排列

    return 0;
}

相关文章:

  • 【多模态大模型】《Qwen2.5-Omni》 论文解读
  • 弥合多层次差距:用于超声心动图中基于文本引导的标签高效分割的双向循环框架|文献速递-深度学习医疗AI最新文献
  • grafana/loki 部署搜集 k8s 集群日志
  • 电脑知识 | TCP通俗易懂详解 <一>
  • Java获取终端设备信息工具类
  • Day 8(下篇):总线驱动模型实战全解析 —— 以 PCA9450 PMIC 为例
  • 【机器学习】每日一讲-朴素贝叶斯公式
  • 超强大小白工具,应用广泛,PDF 删除,无需下载,在线使用,操作超简单,超实用
  • WMware虚拟机Ubuntu磁盘扩容
  • 突破性能瓶颈:Java微服务多任务管理的架构设计与实践
  • 可编辑33页PPT | 经营管理数字化转型平台解决方案
  • MySQL 优化方案大全
  • Acrel-1000DP分布式光伏监控系统在嘉兴亨泰新能源有限公司2996.37KWP分布式光伏项目中的应用
  • STL-函数对象
  • 程序员技能跃迁:职坐标精准赋能
  • 2.2goweb解析http请求信息
  • Colmap的安装和使用
  • Dockerfile部署springboot 项目或者springcloud项目
  • AcWing 6100. 奶牛选美
  • (十三)人工智能应用--深度学习原理与实战--理解卷积神经网络的原理及优势
  • 黄金做空网站/上海seo关键词优化
  • 成都专业做网站的公司/seo门户网
  • 成品网站/整合营销传播成功案例
  • 恩施网站设计/怎么样关键词优化
  • wordpress 代做网站/app推广营销
  • 做一网站需要多少钱/pc端网页设计公司