递归、回溯-算法框架

之前已经学习过回溯法的一些问题,从这篇文章开始,继续深入学习一下回溯法以及其他经典问题。

回溯法有通用的解题法之称。用它可以系统的搜索一个问题的所有解或任一解,回溯法是一个既带有系统性又带有跳跃性的搜索算法。

它的问题的解空间树中,按深度优先策略,从根结点出发搜索解空间树。算法搜索至解空间树的任一结点时,先判断该结点是否包含问题的解。如果肯定不包含,则跳过对以该结点为根的子树的搜索,逐层向其祖先结点回溯。否则,进入该子树,继续按深度优先策略搜索。回溯法求问题的所有解时,要回溯到根,且根结点的所有子树都已被搜索遍才结束。回溯法求问题的一个解时,只要搜索到问题的一个解就可结束。

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

回溯法的算法框架:

1、问题的解空间

用回溯法解问题时,应明确定义问题的解空间。问题的解空间至少应包含问题的一个(最优解)。

2、回溯法的基本思想

确定了解空间的组织结构后,回溯法从开始结点出发,以深度优先方式搜索整个解空间。这个开始结点称为活结点,同时也称为当期那的扩展结点,如果在当前的扩展结点处不能再向纵深方向移动,则当前扩展结点就称为死结点。此时,应往回移动(回溯)至最近的一个或活结点处,并使这个活结点称为当前的扩展结点。回溯法以这种工作方式递归地在解空间中搜索,直至找到所要求的解或解空间中已无活结点时为止。

3、递归回溯

回溯法对解空间作深度优先搜索,因此在一般情况下可用递归函数来实现回溯法如下:

void Backtrack(int t)
{
    if(t > n)                             //t>n时已搜索到一个叶结点,output(x)得到的可行解x进行记录或输出处理
        Output(x);
    else                                  //当前拓展结点是解空间树的内部结点
    {
        for(int i = f(n,t); i <= g(n, t); i++)   //函数f和g分别表示当前扩展结点处未搜索子树的起止编号
        {
            x[t] = h(i);                         //h(i)表示在当前扩展结点处x[t]的第i个可选值
            if(Constraint(t) && Bound(t))
                Backtrack(t+1);
        }                                        //循环结束时,已搜索遍当前扩展结点的所有未搜索子树
    }
}  

其中,形式参数t表示递归深度,即当前扩展结点在解空间树中的深度。n用来控制递归深度,当t>n时,算法已搜索到叶结点,此时,由Output(x)记录或输出得到的可行解x。算法BackTrack的for循环中f(n,t)和g(n,t)分别表示在当前扩展结点处未搜索过的子树的起始编号和终止编号。h(i)表示在当前扩展结点处x[t]的第i个可选值。Constraint(t)和Bound(t)表示在当前扩展结点处的约束函数和限界函数。Constraint(t)返回的值为true时,在当前扩展结点处x[1:t]的取值满足问题的约束条件,否则不满足问题的约束条件,可剪去相应的子树。

Bound(t)返回的值为true时,在当前扩展结点处x[1:t]的取值未使目标函数越界,还需由Backtrack(t+1)对其相应的子树做进一步搜索。

否则,当前扩展结点处x[1:t]的取值使目标函数越界,可剪去相应的子树。执行了算法的for循环后,已搜索遍当前扩展结点的所有未搜索过的子树。Backtrack(t)执行完毕,返回t-1层继续执行,对还没有测试过的x[t-1]的值继续搜索。当t=1时,若已测试完x[1]的所有可选值,外层调用就全部结束。显然,这一搜索过程按深度优先方式进行,调用一次Backtrack(1)即可完成整个回溯搜索过程。

4、迭代回溯

采用树的非递归深度优先遍历算法,也可将回溯法表示为一个非递归的迭代过程如下:

void IterativeBacktrack()
{
    int t;

    t = 1;                                       //当前扩展结点在解空间树中的深度,在这一层确定解向量的第t个分量x[t]的取值
    while(t > 0)
    {
        if(f(n,t) <= g(n,t))                    //f和g分别表示在当前扩展结点处未搜索子树的起止编号
        {
            for(int i = f(n,t); i <= g(n,t); i++)
            {
                x[t] = h(i);                    //h(i)表示在当前扩展结点处x[t]的第i个可选值
                if(Constraint(t) && Bound(t))
                {
                    if(Solution(t))             //solution(t)判断当前扩展结点处是否已得到问题的一个可行解
                        Output(x);
                    else
                        t++;                    //solution(t)为假,则仅得到一个部分解,需继续纵深搜索
                }
            }
        }
        else
            t--;                                //如果f(n,t)>g(n,t),已搜索遍当前扩展结点的所有未搜索子树,
    }                                           //返回t-1层继续执行,对未测试过的x[t-1]的值继续搜索
}       

上述迭代回溯算法中,用Solution(t)判断在当前扩展结点处是否已得到问题的可行解。它返回的值为true时,在当前扩展结点处x[1:t]是问题的可行解。此时,由Output(x)记录或输出得到的可行解。它返回的值为false时,在当前扩展结点处x[1:t]只是问题的部分解,还需向纵深方向继续搜索。

算法中f(n,t)和g(n,t)分别表示在当前扩展结点处未搜索过的子树的起始编号和终止编号。h(i)表示在当前扩展结点处x[t]的第i个可选值。Constraint(t)和Bound(t)是当前扩展结点处的约束函数和限界函数。Constraint(t)的返回的值为true时,在当前扩展结点处x[1:t]的取值满足问题的约束条件,否则不满足问题的约束条件,可剪去相应的子树。Bound(t)返回的值为true时,在当前扩展结点处x[1:t]的取值未使目标函数越界,还需对其相应的子树做进一步搜索。否则,当前扩展结点处x[1:t]的取值已使目标函数越界,可剪去相应的子树。算法的while循环结束后,完成整个回溯搜索过程。

5、字集树与排列树

当所给的问题是从n个元素的集合S中找出满足某种性质的子集时,相应的解空间数称为子集树。这类子集树通常有个叶结点,其结点总个数为.遍历子集树的任何算法均需的计算时间。

void Backtrack(int t)
{
    if(t > n)
        Output(x);
    else
    {
        for(int i = 0; i <= 1; i++)
        {
            x[t] = i;
            if(Constraint(t) && Bound(t))
                Backtrack(t+1);
        }
    }
}

当所给的问题是确定n个元素满足某种性质的排列时,相应的解空间树称为排列树。排列树通常有n!个叶结点。因此遍历排列数需要的计算时间。

void Backtrack(int t)
{
    if(t > n)
        Output(x);
    else
    {
        for(int i = t; i <= n; i++)
        {
            swap(x[t], x[i]);
            if(Constraint(t) && Bound(t))
                Backtrack(t+1);
            swap(x[t], x[i]);
        }
    }
}

在调用Backtrack(1)执行回溯搜索之前,先将变量数组x初始化为单位排列(1,2,....,n)

原文地址:https://www.cnblogs.com/yfr2zaz/p/10569119.html

时间: 2024-09-30 06:35:35

递归、回溯-算法框架的相关文章

回溯0--递归回溯算法框架

递归回溯算法框架 一.心得 3 都是在for下的if下的 4 保存结果,找下一步,回溯,这三个是一起的 5 还有一个到达目的地输出解放在外面就好 search后面的k是轮数  三个数组:原数据数组标记数组储存结果数组 框架二 到目的地的情况要多加1,因为满足了的下一轮就好判断 二.代码 1 /* 2 递归回溯算法框架: 3 都是在for下的if下的 4 保存结果,找下一步,回溯,这三个是一起的 5 还有一个到达目的地输出解放在外面就好 6 */ 7 /* 8 框架一 9 */ 10 int se

java暴力递归回溯算法

今天这个问题是我之前一直想解决的,还记得以前第一次上蓝桥杯的课的时候,也就是大一高数期中模拟考试那天,下午去上蓝桥杯课,遇到这道题,当时写了写,根本没有思路,然后就给大一的模拟考试去了.印象深刻啊,一直没写出来.先来说一下题目吧. 1.问题描述: 如下图所示的数字三角形,编写一个程序计算从顶部到底部某一处的一条路径,使得该路径数字和最大,输出路径和最大值. 7 3      8 8      1      0 2      7      4      4 4      5      2     

穷举递归和回溯算法终结篇

穷举递归和回溯算法 在一般的递归函数中,如二分查找.反转文件等,在每个决策点只需要调用一个递归(比如在二分查找,在每个节点我们只需要选择递归左子树或者右子树),在这样的递归调用中,递归调用形成了一个线性结构,而算法的性能取决于调用函数的栈深度.比如对于反转文件,调用栈的深度等于文件的大小:再比如二分查找,递归深度为O(nlogn),这两类递归调用都非常高效. 现在考虑子集问题或者全排列问题,在每一个决策点我们不在只是选择一个分支进行递归调用,而是要尝试所有的分支进行递归调用.在每一个决策点有多种

回溯算法详解(转)

https://leetcode-cn.com/problems/n-queens/solution/hui-su-suan-fa-xiang-jie-by-labuladong/ 这篇文章是很久之前的一篇<回溯算法详解>的进阶版,之前那篇不够清楚,就不必看了,看这篇就行.把框架给你讲清楚,你会发现回溯算法问题都是一个套路. 废话不多说,直接上回溯算法框架.解决一个回溯问题,实际上就是一个决策树的遍历过程.你只需要思考 3 个问题: 1.路径:也就是已经做出的选择. 2.选择列表:也就是你当前

【八皇后问题】 回溯算法

回溯算法:回溯算法实际上是一个类似枚举的搜索尝试方法,它的思想是在搜索尝试中寻找问题的解,当发现不满足求解条件时,就“回溯”返回,尝试别的路径.之前介绍的基础算法中的贪婪算法,动态规划等都具有“无后效性”,也就是在分段处理问题时,某状态一旦确定,将不再改变.而多数问题很难找到"无后效性”的阶段划分和相应决策,而是通过深入搜索尝试和回溯操作完成的. 八皇后问题:8*8的国际象棋棋盘中放八个皇后,是任意两个皇后不能互相吃掉.规则:皇后能吃掉同一行,同一列,同一对角线的任意棋子. 模型建立:不妨设八个

8皇后以及N皇后算法探究,回溯算法的JAVA实现,非递归,循环控制及其优化

上两篇博客 8皇后以及N皇后算法探究,回溯算法的JAVA实现,递归方案 8皇后以及N皇后算法探究,回溯算法的JAVA实现,非递归,数据结构“栈”实现 研究了递归方法实现回溯,解决N皇后问题,下面我们来探讨一下非递归方案 实验结果令人还是有些失望,原来非递归方案的性能并不比递归方案性能高 代码如下: package com.newflypig.eightqueen; import java.util.Date; /** * 使用循环控制来实现回溯,解决N皇后 * @author [email pr

c语言数据结构:递归的替代-------回溯算法

1.要理解回溯就必须清楚递归的定义和过程. 递归算法的非递归形式可采用回溯算法.主要考虑的问题在于: 怎样算完整的一轮操作. 执行的操作过程中怎样保存当前的状态以确保以后回溯访问. 怎样返回至上一次未执行的操作. 2.贴代码表现: 先序遍历二叉树: BTNode *FindNode(BTNode *b,ElementType x) { //在二叉树中查找值为x的结点 BTNode *p; if (b==NULL) return NULL; else if (b->Element==x) retu

谈谈递归和回溯算法的运用

递归和回溯算法的运用 题目描述 有n个士兵站成一列,从第1个士兵前面向后望去,刚好能看到m个士兵,如果站在后面的士兵身高小于或者等于前面某个士兵的身高,那么后面的这个士兵就不能被看到,问这n个士兵有多少种排列方式,刚好在观测位能看到m个士兵? 第一行输入 n 个士兵和 m 个可以看到的士兵(n >= m),第二行输入 n 个士兵的身高,输出为排列方式的种数. 输入: 4 3 1 1 2 3 输出: 6 也就是说,输入数 n, m (n < m),然后输入 n 个正整数到一个数组 a 中,a 数

DFS ( 深度优先/回溯算法 ) 一

    深度优先搜索算法(英语:Depth-First-Search,简称DFS)是一种用于遍历或搜索树或图的算法. 沿着树的深度遍历树的节点,尽可能深的搜索树的分支.当节点v的所在边都己被探寻过或者在搜寻时结点不满足条件,搜索将回溯到发现节点v的那条边的起始节点.整个进程反复进行直到所有节点都被访问为止.属于盲目搜索,最糟糕的情况算法时间复杂度为O(!n).(Wiki) DFS在搜索过程常常伴随许多优化策略,增加剪枝函数,或者和动态规划结合. 让我们用一道看似简单的例子理解DFS. = = /