• 遞歸回溯解決8皇后問題


    今天學習了經典的8皇后問題,强化了對遞歸調用的理解,之前自己寫遞歸老是把出口條件理解錯,導致邏輯錯誤,時常抛棧溢出的錯誤.

    其實簡單來説遞歸調用,在調用之前,一定要想明白,遞歸出口在哪裏,在調用遞歸的時候,怎麽能讓遞歸的代碼不斷向出口方向靠近,

    最終能找到遞歸的出口.這個問題想明白了,遞歸調用也就成功了一半了.

    之前在調用遞歸時還發現,遞歸説到底是通過棧進行計算,如果在遞歸裏調用了堆裏的東西,一定不能是new出來的對象.

    而且遞歸調用時要盡量把調用遞歸用到的資源放到外面,減少遞歸調用的資源.

    舉個簡單例子來説,寫一個輸出重複語句的遞歸方法,代碼本身沒問題,但是如果調用方法次數過多,仍然會抛出棧溢出的錯誤.

    用代碼測試過,一個簡單的輸出語句,在我電腦上,用String直接調用遞歸方法,一旦超過6200次就可能抛異常,

    用StringBuilder調用遞歸方法,一旦超過12000次也可能抛異常

    今天老師講的遞歸實現8皇后的方法確實很經典.占用資源也不多,但老師說,實際上這樣的算法大概得調用14000多次遞歸方法

    由此可見,其實遞歸方法代碼占用的資源和代碼本身的複雜程度關係並不大,關鍵還是看代碼怎麽設計的,

    怎麽能夠在調用時還能保證不占用過多的計算機資源.

    以上就是我本人對於遞歸的一些粗淺的理解.

    下面放上我重寫過的遞歸解決8皇后問題的代碼.

     1 package com.recursion;
     2 
     3 /*
     4  * 遞歸回溯解決8皇后問題
     5  * 需求,根據象棋的規則,在棋盤上放置8個皇后,每個皇后的下一步棋不能吃子
     6  * 棋盤上放置到第8個皇后時,8個皇后的位置作爲一個正解,算出總共有多少正解
     7  * 
     8  * 遞歸回溯的思路
     9  * 將第一個皇后放在第一行第一列,放第二個皇后,看下一步棋是否能吃子,
    10  * 不能吃子則放第三個皇后,放置下一個子時,位置可能回溯,發生變化
    11  * 放到第8個皇后時開始回溯求解
    12  * 得到一個正確解就會退回一個棧,會把棧裏所有的可能性都嘗試一遍,
    13  * 退回上一個棧,再次求解,一直回溯到第一個皇后
    14  * 
    15  */
    16 public class EightQueen {
    17     //1,共有8個皇后
    18     int max = 8;
    19     //1,定義存放解法的數組
    20     int[] array = new int[max];
    21     //5,定義解法的變量
    22     static int count = 0;
    23     public static void main(String[] args) {
    24         // TODO Auto-generated method stub
    25         //1,用一維數組表示棋盤,索引表示行坐標,數值表示列坐標
    26         //定義一個將皇后放的位置輸出的方法
    27         //4,調用遞歸方法測試
    28         EightQueen queen8 = new EightQueen();
    29         queen8.check(0);
    30         //5,統計共有多少種解法
    31         System.out.printf("一共有%d種解法",count);
    32     }
    33     
    34     //3,定義放置皇后的方法,每一次進入check都會循環遞歸,知道得出正解
    35     private void check(int n) {
    36         //n為8時,方法為正解,遞歸出口
    37         if (n == max) {
    38             print();
    39             return;
    40         }
    41         //放置皇后,判斷是否衝突
    42         for (int i = 0; i < max; i++) {
    43             //把皇后n放到第一列
    44             array[n] = i;
    45             if (judge(n)) {
    46                 //如果不冲突,開始放第n+1個皇后,開始遞歸
    47                 check(n + 1);
    48             }
    49             //如果衝突,遞歸繼續循環,去下一行
    50         }
    51     }
    52     
    53     //2,定義查看放置第n個皇后是否滿足放置規則的方法
    54     /**
    55      * 
    56      * @param n 表示第n個皇后
    57      * @return
    58      */
    59     private boolean judge(int n) {
    60         for (int i = 0; i < n; i++) {
    61             //判斷第n個皇后和第n-1個皇后是否在同一列,n本身在自增運算,不需要判斷行
    62             //判斷第n個皇后和第i個皇后是否在同一斜綫
    63             if (array[i] == array[n] || Math.abs(n - i) == Math.abs(array[n] - array[i])) {
    64                 return false;
    65             } 
    66         }
    67         return true;
    68     }
    69     
    70     //1,定義打印輸出的方法
    71     private void print() {
    72         count++;
    73         for (int i = 0; i < array.length; i++) {
    74             System.out.print(array[i] + " ");
    75         }
    76         System.out.println();
    77     }
    78 
    79 }

    代碼不多,卻解決了一個比較複雜的經典計算問題.

  • 相关阅读:
    RAID卡是否有(启用)缓存对“随机读写”性能有巨大的影响。
    《C++程序设计实践与技巧:测试驱动开发》 环境搭建遇到的坑
    c++ 实现 cout 示例
    c++ 文件
    js 鼠标事件模拟
    eclipse c++ 配置 c++ 17
    c++ 17 vector中string的性能问题 std::vector<std::string> string vs string_view
    c++ 17 模板
    C++17剖析:string在Modern C++中的实现
    编译程序加不加 -lpthread 的区别
  • 原文地址:https://www.cnblogs.com/zzzzzpaul/p/11588020.html
Copyright © 2020-2023  润新知