Java版高级数据结构算法 - 回溯算法之排列树(八皇后问题)

知识的学习在于点滴记录,坚持不懈;知识的学习要有深度和广度,不能只流于表面,坐井观天;知识要善于总结,不仅能够理解,更知道如何表达!

排列树

解空间就是问题所有解的可能取值构成的空间,一个问题的解往往包含了得到这个解的每一步,就是对应解空间树中一条从根节点到叶子节点的路径。

排列树就代表了一类问题的解空间,它并不是真实存在的数据结构,也就是说并不是真的有一颗这样的树,只是抽象出来的解的空间树。

当问题求解的结果是集合S的元素的某一种排列的时候,其对应的解空间就是排列树,时间复杂度是 O ( n ! ) O(n!) O(n!),同样可以通过适当的剪枝函数来提供排列树的遍历效率。代码示例如下:

public static void main(String[] args) {
int[] arr = {1,2,3};
    backtrace(arr, 0, arr.length);
}

private static void swap(int[] arr, int i, int j){
    int tmp = arr[i];
    arr[i] = arr[j];
    arr[j] = tmp;
}

private static void backtrace(int[] arr, int i, int length) {
    if(i == length){
        System.out.println(Arrays.toString(arr));
    } else {
        for (int j = i; j < length; j++) {
            swap(arr, i, j);
            backtrace(arr, i+1, length);
            swap(arr, i, j);
        }
    }
}

这段代码运行的结果,其结果就构成了一颗排列树,如下图所示:
在这里插入图片描述

手写八皇后问题代码

还记得是2018届的一位理工同学,和邮电的几位同学一起,去面试某某世界的时候,在面试最后有一个代码题需要输出,邮电的同学写的都是一个简单的双链表的删除,而到他就是手写八皇后问题的代码,感觉十分崩溃,也因为那时候没有给大家上这一部分的内容,其实八皇后的问题,只要理解了排列树这个解空间的生成过程,其代码是非常简单的。

八皇后问题:按照国际象棋规则,皇后可以攻击与之处在同一行,或者同一列,或者同一斜线上的其它棋子。现在有n个皇后放置在nxn格的棋盘上,如何摆放n个皇后而使它们都不能互相吃子?有多少种摆法?

八皇后问题,实际上就是要求输出对原序列元素的一种特殊的排列方式,实现代码如下:

public class Queen {
    public static void main(String[] args) {
        // 数组元素下标代表行数,元素内容代表列数,表示皇后棋子放至的行列数
        int[] arr = {1,2,3,4,5,6,7,8};
        backtrace(arr, 0, arr.length);
        System.out.println("总的排列数:" + count);
    }

    private static void swap(int[] arr, int i, int j){
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }

    static int count = 0; // 记录一下总的排列数
    private static void backtrace(int[] arr, int i, int length) {
        if(i == length){
            count++;
            System.out.println(Arrays.toString(arr));
        } else {
            for (int j = i; j < length; j++) {
                swap(arr, i, j);
                if(line(arr, i)){
                    backtrace(arr, i+1, length);
                }
                swap(arr, i, j);
            }
        }
    }

    private static boolean line(int[] arr, int i) {
        // 检查从第0行开始,到第i行,不能出现同行i==j,同列arr[i]==arr[j],同斜线的棋子
        for (int j = 0; j < i; j++) {
            if(i == j && arr[i] == arr[j] 
                && Math.abs(arr[i] - arr[j]) == Math.abs(i - j)){
                return false;
            }
        }
        return true;
    }
}

运行上面的代码,可以看出结果,总共是92种摆法。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值