《數(shù)據(jù)結(jié)構(gòu)與算法之美》30——回溯算法

什么是回溯算法

回溯算法實(shí)際上一個(gè)類似枚舉的搜索嘗試過程,主要是在搜索嘗試過程中尋找問題的解,當(dāng)發(fā)現(xiàn)已不滿足求解條件時(shí),就 “回溯” 返回,嘗試別的路徑。

回溯法是一種選優(yōu)搜索法,按選優(yōu)條件向前搜索,以達(dá)到目標(biāo)。但當(dāng)探索到某一步時(shí),發(fā)現(xiàn)原先選擇并不優(yōu)或達(dá)不到目標(biāo),就退回一步重新選擇,這種走不通就退回再走的技術(shù)為回溯法,而滿足回溯條件的某個(gè)狀態(tài)的點(diǎn)稱為 “回溯點(diǎn)”。許多復(fù)雜的,規(guī)模較大的問題都可以使用回溯法,有“通用解題方法”的美稱。

回溯算法的基本思想是:從一條路往前走,能進(jìn)則進(jìn),不能進(jìn)則退回來,換一條路再試。

八皇后問題

我們學(xué)習(xí)了什么是回溯算法,聽起來很簡單,但具體怎么使用呢?下面我們通過一個(gè)例子來說明回溯算法的用法。

八皇后問題:有一個(gè)8X8的棋盤,希望往里放8個(gè)棋子(皇后),每個(gè)棋子所在的行、列、對角線都不能有另一個(gè)棋子,找到所有滿足這種要求的放棋方式。

解題思路:把這個(gè)問題劃分成8個(gè)階段,依次將8個(gè)棋子放到第一行、第二行、第三行....第八行。在放置的過程中,不停地檢查當(dāng)前的方法,是否滿足要求。如果滿足,則跳到下一行繼續(xù)放置棋子;如果不滿足,那就換一種方法,繼續(xù)嘗試。

代碼實(shí)現(xiàn)如下:

public class Solution {
    int[] result = new int[8]; // 全局或成員變量,下標(biāo)表示行,值表示queen存儲在哪一列
    public void Cal8Queens (int row) { // 調(diào)用方式:Cal8Queens(0);
        if (row == 8) { // 8個(gè)棋子都放置好了,打印結(jié)果
            PrintQueens (result);
            return; // 8行棋子都放好了,已經(jīng)沒法再往下遞歸了,所以就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行l(wèi)eftup列有棋子嗎?
                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) { // 打印出一個(gè)二維矩陣
        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();
    }
}

總結(jié)

回溯算法的思想非常簡單,大部分情況下,都是用來解決廣義的搜索問題:從一組可能的解中,選擇出一個(gè)滿足要求的解。

回溯算法非常適合用遞歸來實(shí)現(xiàn),在實(shí)現(xiàn)的過程中,剪枝操作是提高回溯效率的一種技巧。利用剪枝,并不需要窮舉搜索所有的情況,從而提高搜索效率。

參考資料

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容