递归和回溯(recursion and backtracking)

1. Concept

Backtracking is a refinement of the brute force approach, which systematically searches for a solution to a problem among all available options. It does so by assuming that the solutions are represented by vectors (v1, ..., vm) of values and by traversing, in a depth first manner, the domains of the vectors until the solutions are found.

When invoked, the algorithm starts with an empty vector. At each stage it extends the partial vector with a new value. Upon reaching a partial vector (v1, ..., vi) which can’t represent a partial solution, the algorithm backtracks by removing the trailing value from the vector, and then proceeds by trying to extend the vector with alternative values.

The classic textbook example of the use of backtracking is the eight queens puzzle that asks for all arrangements of eight chess queens on a standard chessboard so that no queen attacks any other.

2. Examples

1) a simple example

recursion:

    public int fun(int a, int b){
        if(a > 5){
            return 2;
        }else{
            int c = fun(a+1, b*2) + a + b;
            return c;
        }
    } 

backtracking:

public int backtracking(int a, int b){
        Stack<Integer> stack = new Stack<Integer>();
        int status = 0;
        int sum = 0;
        while(!stack.empty() || status == 0){
            if(status == 0){
                if( a > 5){
                    status = 1;
                    sum = 2;
                }else{
                    stack.push(a);
                    stack.push(b);
                    a = a + 1;
                    b = b * 2;
                    status = 0;
                }
            }else{
                b = stack.pop();
                a = stack.pop();
                sum += a + b;
                status = 1;
            }
        }
        return sum;

    }

2) inorder traverse of a binary tree

recursion:

    public void inorder(Node root){
        if(root == null) return;
        inorder(root.leftChild);
        System.out.print(root.data + "  ");
        inorder(root.rightChild);
    }

backtracking:

public static void backtracking(Node root){
        Stack<Node> stack = new Stack<Node>();
        boolean status1 = true;
        while(!stack.empty() || status1 == true){
            if(status1 == true){
                if(root == null){
                    status1 = false;
                    continue;
                }
                stack.push(root);
                root = root.leftChild;
                status1 = true;
            }else{
                root = stack.pop();
                System.out.print(root.data + "  ");
                root = root.rightChild;
                status1 = true;
            }
        }

    }

if we add a statement after the second recursion function as follows:

    public void inorder(Node root){
        if(root == null) return;
        inorder(root.leftChild);
        System.out.print(root.data + "  ");
        inorder(root.rightChild);
        System.out.print("  finish  ");
    }

then we need to decide where to return

    public void backtracking(Node root){
        Stack<Node> stack = new Stack<Node>();
        Stack<Integer> statusStack = new Stack<Integer>();
        int status = 0;
        while(!stack.empty() || status == 0){
            if(status == 0){
                if(root == null){
                    status = statusStack.pop();
                    continue;
                }
                stack.push(root);
                root = root.leftChild;
                statusStack.push(1);
                status = 0;
            }else if(status == 1){
                root = stack.pop();
                System.out.print(root.data + "  ");
                root = root.rightChild;
                stack.push(root);
                statusStack.push(2);
                status = 0;
            }else{
                stack.pop();
                if(!statusStack.empty()){
                    status = statusStack.pop();
                }
                System.out.print("  finish  ");
            }
        }

    }

3) quicksort

recursion:

    public  int partition(int[] arr, int begin, int end){
        int pivot = arr[begin + (end-begin)/2];
        while(begin < end){
            while(arr[begin] < pivot){
                begin++;
            }
            while(arr[end] > pivot){
                end--;
            }
            if(begin <= end){
                int temp = arr[begin];
                arr[begin] = arr[end];
                arr[end] = temp;
                begin++;
                end--;
            }
        }
        return begin;
    }

    public void quicksort(int[] arr, int begin, int end){
        int pivot = partition(arr, begin, end);
        if(pivot-1 > begin){
            quicksort(arr, begin, pivot -1);
        }
        if(pivot < end){
            quicksort(arr, pivot, end);
        }
    }

backtracking:

public void bactrackingk(int[] arr, int begin, int end){
        Stack<Integer> stack = new Stack<Integer>();
        int status = 0;
        while(!stack.empty() || status == 0){
            if(status == 0){
                int pivot = partition(arr, begin, end);
                if(pivot-1 <= begin){
                    status = 1;
                    continue;
                }
                stack.push(end);
                stack.push(pivot);
                end = pivot - 1;
                status = 0;
            }else{
                int pivot = stack.pop();
                end = stack.pop();
                begin = stack.pop();
                if(pivot > end){
                    status = 1;
                    continue;
                }
                begin = pivot - 1;
                status = 0;
            }
        }

    }

more:

http://en.wikipedia.org/wiki/Backtracking

http://web.cse.ohio-state.edu/~gurari/course/cis680/cis680Ch19.html

http://www.cnblogs.com/steven_oyj/archive/2010/05/22/1741376.html

时间: 2024-12-31 05:51:30

递归和回溯(recursion and backtracking)的相关文章

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

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

递归,回溯,DFS,BFS的理解和模板【摘】

递归:就是出现这种情况的代码: (或者说是用到了栈) 解答树角度:在dfs遍历一棵解答树 优点:结构简洁缺点:效率低,可能栈溢出 递归的一般结构: 1 void f() { 2 if(符合边界条件) { 3 /////// 4 return; 5 } 6 7 //某种形式的调用 8 f(); 9 } 回溯:递归的一种,或者说是通过递归这种代码结构来实现回溯这个目的.回溯法可以被认为是一个有过剪枝的DFS过程.解答树角度:带回溯的dfs遍历一棵解答树回溯的一般结构: 1 void dfs(int

这是递归和回溯的算法 是abcd&#39;的排列可能

#include<stdio.h>#include<iomanip>#include<iostream>using namespace std;bool b[10]={0};int a[10]={0};int print(){ for (int i=1;i<=4;i++) printf("%c ",a[i]); printf("\n");} int dosomething(int z){ int mm; for ( mm=1

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

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

LeetCode 31:递归、回溯、八皇后、全排列一篇文章全讲清楚

本文始发于个人公众号:TechFlow,原创不易,求个关注 今天我们讲的是LeetCode的31题,这是一道非常经典的问题,经常会在面试当中遇到.在今天的文章当中除了关于题目的分析和解答之外,我们还会详细解读深度优先搜索和回溯算法,感兴趣的同学不容错过. 链接 Next Permutation 难度 Medium 描述 实现C++当中经典的库函数next permutation,即下一个排列.如果把数组当中的元素看成字典序的话,那下一个排列即是字典序比当前增加1的排列.如果已经是字典序最大的情况

用递归和回溯法实现八皇后问题

问题描述: 八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例.该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋棋盘上放置八个皇后,使得任意两个皇后不能互相攻击,即任何行.列或对角线(与水平轴夹角为45°或135°的斜线)上不得有两个或两个以上的皇后.对于这个问题数学家高斯认为有76种方案.1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果.计算机发明后,有多种计算机语言可以解决此问题. 问题分析 在这里我们可以声明一个

递归和回溯的区别

递归是一种算法结构,回溯是一种算法思想一个递归就是在函数中调用函数本身来解决问题回溯就是通过不同的尝试来生成问题的解,有点类似于穷举,但是和穷举不同的是回溯会"剪枝",意思就是对已经知道错误的结果没必要再枚举接下来的答案了,比如一个有序数列1,2,3,4,5,我要找和为5的所有集合,从前往后搜索我选了1,然后2,然后选3 的时候发现和已经大于预期,那么4,5肯定也不行,这就是一种对搜索过程的优化

递归、回溯-算法框架

之前已经学习过回溯法的一些问题,从这篇文章开始,继续深入学习一下回溯法以及其他经典问题. 回溯法有通用的解题法之称.用它可以系统的搜索一个问题的所有解或任一解,回溯法是一个既带有系统性又带有跳跃性的搜索算法. 它的问题的解空间树中,按深度优先策略,从根结点出发搜索解空间树.算法搜索至解空间树的任一结点时,先判断该结点是否包含问题的解.如果肯定不包含,则跳过对以该结点为根的子树的搜索,逐层向其祖先结点回溯.否则,进入该子树,继续按深度优先策略搜索.回溯法求问题的所有解时,要回溯到根,且根结点的所有

递归与回溯

递归的一般结构: void f(){ if(符合的条件){ ... return; } 某种形式的调用 f() } 回溯的一般结构: void dfs(T t,...){ if(当前状态为边界状态) { 记录或输出 return; } for(i=0;i<n;i++) //横向遍历解答树所有子节点 { //扩展出一个子状态. 修改了全局变量 if(子状态满足约束条件) { dfs(子状态) } 恢复全局变量//回溯部分 } } 原文地址:https://www.cnblogs.com/wkcod