回溯法与树的遍历

关于回溯法和DFS做下总结:

在程序设计中有一类题目求一组解或者求全部解或者求最优解等系列问题,不是根据某种特定的规则来计算,而是通过试探和回溯的搜索来查找结果,通常都会设计为递归形式.

这类题本身是一颗状态树,当只有两种情况的时候则为二叉树,这棵树不是之前建立的,而是隐含在遍历过程中的.接下来根据一些题目来提高认识.


一.二叉状态树

题目:

说白了就是一个全遍历的过程,找出每一种可能的组合.对于123则有题目中的8种情况.

思路:

这样的全排列问题可以从元素本身入手,每一个元素只有两种状态,被选择和不被选择,那么就可以用一颗二叉树来表示

0表示未选择

那么递归程序就可以设计如下

public class Test10 {

    static String str = "";
    public static void main(String[] args) {
        int[] A = {1,2,3};
        DFS(A, 0);
    }
    /**
     * @param a 存放要遍历的集合
     * @param n 代表当前选择第n个元素
     */
    private static void DFS(int[] a,int n){
        if (n >= a.length) {
            if (str.isEmpty()) {
                System.out.println("$");//集合为空的话设置为$符号
            }else {
                System.out.println(str);
            }
        }else {
            //不选择当前值
            DFS(a, n+1);
            //选择当前值
            String strTemp = new String(str);//新建一个变量,为了回溯时可以退回到之前的数据
            str = str+a[n];
            DFS(a, n+1);
            str = strTemp;//回溯到之前的str状态
        }
    }
}

二.四皇后问题(可扩展为n皇后)

四皇后如果用回溯法的话,会先生一个四叉树,和字典树比较类似了,如下图

可以看出来和上面一样的情况,对于四皇后,每一次选择都有四种放置棋子的方式,所以只要一个一个放置就可以找出全部可以放置的结果了.

对于有重复的,直接截枝,也就是没必要再往下寻找了

对于n皇后,把数组中的4改为n的值就好了

import java.util.Arrays;

public class Test11 {

    private static int A[] = new int[4+1];//四皇后是4*4的格子,为了便于观看,0处不要

    private static int count = 0;

    public static void main(String[] args) {
        DFS(1);
        System.out.println(count);
    }

    /**
     * @param n 表示要放置的行数
     */
    private static void DFS(int n){
        if (n >= A.length) {
            count++;
            printArr();
        }else {
            //对于每一个行,棋子都有四种放置状态
            for (int j = 1; j < A.length; j++) {
                if (check(n, j)) {//如果可以放置
                    A[n] = j;//放置棋子
                    DFS(n+1);//放置下一行
                }
                A[n] = 0;//回溯时,拿掉棋子
            }
        }

    }
    //打印数组
    private static void printArr(){
            System.out.println(Arrays.toString(A));
        System.out.println("-------------------------------------");
    }
    /*
     *检测是否符合条件
     *j==A[i] 检测当前列上有没有棋子
     *Math.abs(n-i) == Math.abs(j - A[i] 检测斜线上有没有棋子
     */
    private static boolean check(int n,int j){
        for (int i = 1; i < n; i++) {
            if (j==A[i] || Math.abs(n-i) == Math.abs(j - A[i])) {
                return false;
            }
        }
        return true;
    }
}

三.poj1416

题目:

现在你要研发一种新型的碎纸机,待粉碎的纸上面有一串数字,要求把纸粉碎成的几片上的数字的和尽量接近而不能超过给定的数字target number。比如:一片纸上的数字为12346,target number为50,那么就可以把纸粉碎为1、2、34、6,其加和为43,是所有粉碎方法中最接近50而不超过50的最优解。

解释:

题目换句话来理解就是有一串数字中间用+号来连接,使其结果小于或等于指定值的最优解.

EG:

题目 50 12346

那么可以分为以下几种情况

50 1+2+3+4+6=16

50 12+3+4+6=25

50 123+4+6=133

50 1+23+4+6=34



这道题就可以利用第一题二叉状态树想法来考虑,对于数字之间的空,可以选择插入+号和不插入加号

之前也做过这道题,思路不一样,以前的做法地址如下:

http://blog.csdn.net/u012706811/article/details/51245903

public class Test12 {

    private static int A[] = {1,2,3,4,6};

    private static int total = 50;
    private static int max = 0;
    private static String maxStr = null;
    private static StringBuilder builder = new StringBuilder();

    public static void main(String[] args) {
        DFS(0, "");
        System.out.println(max);
        System.out.println(maxStr);
    }

    private static void DFS(int n,String sum){
        if (n == A.length) {
            int sumtemp = getSum(sum);
            if (sumtemp <= total) {
                if (sumtemp > max) {
                    max = sumtemp;//保存最大值
                    maxStr = sum;//保存最大值对应的串
                }
            }
        }else {
            if (getSum(sum+A[n]) <= total) {//剪枝操作
                DFS(n+1,sum+A[n]);//不加+号
            }
            if (getSum(sum + "+" + A[n]) <= total) {
                DFS(n+1, sum + "+" + A[n]);//加+号
            }
        }
    }
    /**
     * 根据字符串计算出其数值大小
     * @param str
     * @return
     */
    private static int getSum(String str){
        String[] arr = str.split("\\+");//不能直接写+号,需要转义下
        int sum = 0;
        for (String temp : arr) {
            if (temp != null && !"".equals(temp)) {
                sum = sum + Integer.parseInt(temp);
            }
        }
        return sum;
    }

}

程序没问题,但是循环次数比以前做法多了几趟,还没找到原因.

时间: 2025-01-18 08:41:20

回溯法与树的遍历的相关文章

javascript实现数据结构: 树和二叉树的应用--最优二叉树(赫夫曼树),回溯法与树的遍历--求集合幂集及八皇后问题

赫夫曼树及其应用 赫夫曼(Huffman)树又称最优树,是一类带权路径长度最短的树,有着广泛的应用. 最优二叉树(Huffman树) 1 基本概念 ① 结点路径:从树中一个结点到另一个结点的之间的分支构成这两个结点之间的路径. ② 路径长度:结点路径上的分支数目称为路径长度. ③ 树的路径长度:从树根到每一个结点的路径长度之和. 以下图为例: A到F :结点路径 AEF : 路径长度(即边的数目) 2 : 树的路径长度:3*1+5*2+2*3=19: ④ 结点的带权路径长度:从该结点的到树的根结

回溯法 子集树和排序树

当所给问题是从n个元素的集合S中找出满足某种性质的子集时,解空间为子集树.例如:0-1背包问题 当所给问题是从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);

算法复习笔记(回溯法,分支限界法)

回溯法 分支限界法 回溯法 回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标.但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法. 基本思想: 在包含问题的所有解的解空间树中,按照深度优先搜索的策略,从根结点出发深度探索解空间树.当探索到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发继续探索下去,如果该结点不包含问题的解,则逐层向其祖先结点回溯.(其实回溯法就是对隐式图的深度优先搜索

回溯法理解

回溯法 基本思想: 构建问题的解空间树,在其解空间树中,从根节点出发,进行深度优先搜索.在搜索过程中,对解空间 树的每个结点进行判断,判断该结点是否包含问题的解,若肯定不包含,则跳过对以该结点为根的子树的 搜索,逐层向其祖先结点回溯.否则,则进入该子树,继续按深度优先策略搜索. 步骤: 1.针对所给问题,定义其解空间 2.确定易于搜索的解空间结构 3.深度优先搜索其解空间,并在搜索过程中用剪枝函数避免无效搜索 剪枝: 回溯法搜索空间树时,常用限界函数和约束函数避免无效搜索,其中约束函数将不满足约

那些妖术——树的遍历

这个方法有点邪门,和大家在课堂上学的有点不一样,所以blog的名字取得有点邪乎. 一般的程序员应聘技术类的笔试都会有一道题目,那就是树的遍历(前序遍历,中序遍历和后续遍历).这里教大家玩点新鲜的, 可能和平时大家学的有点不一样.但是绝对是在考场上解决问题的神器,因为一个字快,可以帮你节省时间做其他的题目. 喜欢的小伙伴记得点赞啊.(*^__^*) 一.中序遍历 这个是最简单的,什么是中序遍历,那就是把我们的树压扁了就可以得到我们的中序遍历,所以中序遍历就是DBGEHACIJF. 什么是把树压扁了

回溯法小实例

1.图的m着色问题: 1 /* 2 *问题描述:给定无向连通图G和m种不同的颜色.用这些颜色为图G的各个顶点着色,每个顶点着一种颜色.是否有一种着色法使G中每条边的两个顶点着不同的颜色. 3 * 这个问题是图的m可着色判定问题.若一个图最少需要m中颜色才能使图中每条边连接的2个顶点着不同的颜色,则称这个数m为该图的色数. 4 *算法分析:给定图G=(V,E)和m中颜色,如果这个图不是m可着色,给出否定回答:如果这个图是m可着色的,找出所有不同的着色法. 5 * 回溯法+子集树 6 * 问题的解空

Letter Combinations of a Phone Number——解题报告 (回溯法的应用 )

[题目] Given a digit string, return all possible letter combinations that the number could represent. A mapping of digit to letters (just like on the telephone buttons) is given below. Input:Digit string "23" Output: ["ad", "ae"

看数据结构写代码(33) 树与回溯法(一) 子集树

回溯法 是 一种 在 穷举 中,裁剪 不满足 条件 的 分支,已达到 提高 效率的 方法.其基本原型 是 树的 先序遍历,从 树根 到 树叶的路径 是 问题的 一个 解. 回溯法的基本框架 =  确定 解空间 + 深度优先遍历 + 裁剪函数 + 确定结果函数 其中 解空间,分为 子集树 和 排序树. 具体 概念 详解:参考 点击打开链接  和 点击打开链接 递归算法通用 模板如下: 回溯法对解空间作深度优先搜索,因此,在一般情况下用递归方法实现回溯法. // 针对N叉树的递归回溯方法 void

【Algorithm】回溯法与深度优先遍历的异同

1.相同点: 回溯法在实现上也是遵循深度优先的,即一步一步往前探索,而不像广度优先那样,由近及远一片一片地扫. 2.不同点 (1)访问序 深度优先遍历: 目的是“遍历”,本质是无序的.也就是说访问次序不重要,重要的是都被访问过了. 可以参见题Surrounded Regions,深度优先只需要把从边界起始的'O'全部访问到即可. 因此在实现上,只需要对于每个位置记录是否被visited就足够了. 回溯法: 目的是“求解过程”,本质是有序的.也就是说必须每一步都是要求的次序. 可以参见题Word