• 《数据结构与算法之美》30——回溯算法


    什么是回溯算法

    回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就 “回溯” 返回,尝试别的路径。

    回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为 “回溯点”。许多复杂的,规模较大的问题都可以使用回溯法,有“通用解题方法”的美称。

    回溯算法的基本思想是:从一条路往前走,能进则进,不能进则退回来,换一条路再试。

    八皇后问题

    我们学习了什么是回溯算法,听起来很简单,但具体怎么使用呢?下面我们通过一个例子来说明回溯算法的用法。

    八皇后问题:有一个8X8的棋盘,希望往里放8个棋子(皇后),每个棋子所在的行、列、对角线都不能有另一个棋子,找到所有满足这种要求的放棋方式。

    解题思路:把这个问题划分成8个阶段,依次将8个棋子放到第一行、第二行、第三行....第八行。在放置的过程中,不停地检查当前的方法,是否满足要求。如果满足,则跳到下一行继续放置棋子;如果不满足,那就换一种方法,继续尝试。

    代码实现如下:

    public class Solution {
        int[] result = new int[8]; // 全局或成员变量,下标表示行,值表示queen存储在哪一列
        public void Cal8Queens (int row) { // 调用方式:Cal8Queens(0);
            if (row == 8) { // 8个棋子都放置好了,打印结果
                PrintQueens (result);
                return; // 8行棋子都放好了,已经没法再往下递归了,所以就return
            }
            for (int column = 0; column < 8; ++column) { // 每一行都有8中放法
                if (IsOK (row, column)) { // 有些放法不满足要求
                    result[row] = column; // 第row行的棋子放到了Column列
                    Cal8Queens (row + 1); // 考察下一行
                }
            }
        }
    
        private bool IsOK (int row, int column) { // 判断row行column列放置是否合适
            int leftup = column - 1, rightup = column + 1;
            for (int i = row - 1; i >= 0; --i) { // 逐行往上考察每一行
                if (result[i] == column) return false; // 第i行的column列有棋子吗?
                if (leftup >= 0) { // 考察左上对角线:第i行leftup列有棋子吗?
                    if (result[i] == leftup) return false;
                }
                if (rightup < 8) { // 考察右上对角线:第i行rightup列有棋子吗?
                    if (result[i] == rightup) return false;
                }
                --leftup;
                ++rightup;
            }
            return true;
        }
    
        private void PrintQueens (int[] result) { // 打印出一个二维矩阵
            for (int row = 0; row < 8; ++row) {
                for (int column = 0; column < 8; ++column) {
                    if (result[row] == column) Console.Write ("Q ");
                    else Console.Write ("* ");
                }
                Console.WriteLine ();
            }
            Console.WriteLine();
        }
    }
    

    总结

    回溯算法的思想非常简单,大部分情况下,都是用来解决广义的搜索问题:从一组可能的解中,选择出一个满足要求的解。

    回溯算法非常适合用递归来实现,在实现的过程中,剪枝操作是提高回溯效率的一种技巧。利用剪枝,并不需要穷举搜索所有的情况,从而提高搜索效率。

    参考资料

  • 相关阅读:
    ubuntu14.04下安装node.js
    用U盘制作启动盘后空间变小的恢复方法,清除U盘启动盘空间
    JQuery入门——进度条
    setTimeout()与setInterval()——走马灯效果
    JavaScript日期控件,用select实现
    JavaScript获取元素CSS属性
    JavaScript为select添加option,select选项变化时的处理,获取slelect被选中的值
    Javascript为元素添加事件处理函数
    Javascript字数统计
    Struts2文件上传,以及各种注意事项
  • 原文地址:https://www.cnblogs.com/liang24/p/13401922.html
Copyright © 2020-2023  润新知