回溯法求全排列-非递归形式-代码还没优化过

来源:互联网 发布:2017最新淘宝口令红包 编辑:程序博客网 时间:2024/05/01 20:19

回溯法递归的很好写,非递归的难写很多很多!


(看之前必须得理解递归形式的回溯法)


难写在哪?

因为回溯法递归,每次递归都有一个for循环,可以完美避开重复的路径。然而非递归的避免重复路径还要另花心思,这就是他的难点。


如何规避重复路径?

我们通过查找的时候,添加一个条件,就是回溯回来的时候,这个数不光要取没被使用的数,还要取一个比之前这个位置上的数更大的数,因为我们是从1234.。这样增序开始的,所以回溯的时候,当前位置上的值只有可能更大,不可能更小。


规避的时候还要注意一个小细节:

回溯的时候,要把后面的数都置为0,防止你之前的运算,对你后面的规避判断产生干扰。


(所以说代码还没优化过,以后会优化,不过这一次代码注释的很仔细,还是很容易看懂的)


部分代码:

private void sort2() {    int level = 0;    boolean ifFind = false;    while (level >= 0) {        //每换一次位,即深入或者回溯,都要用for循环找一次        for (int i = 0; i < n; i ++) {            //找到的条件是:1.这个数没有被前面的数字使用2.考虑回溯回来的时候,你一定要比刚刚用过的数大,这是避开走过的路的办法            if (flag[i] && current[level] < i + 1) {                //找到了,就放置这个数,当前的数就被占用了,把当前位置的数不可用                flag[i] = false;                //放置这个数,即赋值                current[level] = i + 1;                //如果当前操作的数没有到达最后一位,就继续向下探索                if (level < n - 1) {                    level += 1;                } else if (level == n - 1) {                    //如果到了最后一位,就输出(回溯的情况:不把最后一位区别对待,和前面的回溯一样)                    System.out.println(Arrays.toString(current));                }                //本次找到了                ifFind = true;                break;            }            //如果这个for循环找完了,没有提前break,本次没找到            ifFind = false;        }        //如果找到了,就继续探寻;如果没找到,就必须要回溯了        if (!ifFind) {            //让当前操作数左移一位            level -= 1;            //如果这一次回溯,当前操作数变成负数,那就是第一位都已经没东西可排了,就真的结束了            if (level < 0) {                break;            }            //如果当前操作数还没有变成负数,那就是还需要继续排。把当前的值和下一个值恢复为可用状态            else {                flag[current[level + 1] - 1] = true;                flag[current[level] - 1] = true;                //把当前位置后面的数都置位0,意思就是回溯一位后,在进行寻找的时候,防止后面的数被之前的数干扰到                for (int i = level + 1; i <= n - 1; i++) {                    current[i] = 0;                }            }        }    }}

所有代码:

public class BackTracking {    private int n;    private int[] current;    private boolean[] flag;    BackTracking(int n) {        this.n = n;        current = new int[n];        flag = new boolean[n];        for (int i = 0; i < n; i ++) {            flag[i] = true;            current[i] = 0;        }    }    private void sort2() {        int level = 0;        boolean ifFind = false;        while (level >= 0) {            //每换一次位,即深入或者回溯,都要用for循环找一次            for (int i = 0; i < n; i ++) {                //找到的条件是:1.这个数没有被前面的数字使用2.考虑回溯回来的时候,你一定要比刚刚用过的数大,这是避开走过的路的办法                if (flag[i] && current[level] < i + 1) {                    //找到了,就放置这个数,当前的数就被占用了,把当前位置的数不可用                    flag[i] = false;                    //放置这个数,即赋值                    current[level] = i + 1;                    //如果当前操作的数没有到达最后一位,就继续向下探索                    if (level < n - 1) {                        level += 1;                    } else if (level == n - 1) {                        //如果到了最后一位,就输出(回溯的情况:不把最后一位区别对待,和前面的回溯一样)                        System.out.println(Arrays.toString(current));                    }                    //本次找到了                    ifFind = true;                    break;                }                //如果这个for循环找完了,没有提前break,本次没找到                ifFind = false;            }            //如果找到了,就继续探寻;如果没找到,就必须要回溯了            if (!ifFind) {                //让当前操作数左移一位                level -= 1;                //如果这一次回溯,当前操作数变成负数,那就是第一位都已经没东西可排了,就真的结束了                if (level < 0) {                    break;                }                //如果当前操作数还没有变成负数,那就是还需要继续排。把当前的值和下一个值恢复为可用状态                else {                    flag[current[level + 1] - 1] = true;                    flag[current[level] - 1] = true;                    //把当前位置后面的数都置位0,意思就是回溯一位后,在进行寻找的时候,防止后面的数被之前的数干扰到                    for (int i = level + 1; i <= n - 1; i++) {                        current[i] = 0;                    }                }            }        }    }    public static void main(String[] args) throws Exception {        BackTracking backTracking = new BackTracking(4);        backTracking.sort2();    }}

原创粉丝点击