[回溯算法]python解決N皇后問題(20行代碼)

如果讀者對于回溯算法思路解法還不是很了解,可以先點擊鏈接查閱我之前的一篇博文《算法之【回溯算法】詳解》底哗,很詳細的介紹了回溯算法求解思路及方法季二,有利于你更好的學(xué)習(xí)回溯算法讶请。

題目描述

n 皇后問題研究的是如何將 n 個皇后放置在 n×n 的棋盤上蘸吓,并且使皇后彼此之間不能相互攻擊。 給定一個整數(shù) n笛园,返回所有不同的 n 皇后問題的解決方案涂邀。每一種解法包含一個明確的 n 皇后問題的棋子放置方案瘟仿,該方案中 'Q' 和 '.' 分別代表了皇后和空位。

注:下圖為 8 皇后問題的一種解法比勉。

皇后彼此不能相互攻擊劳较,也就是說:任何兩個皇后都不能處于同一條橫行驹止、縱行或斜線上。

N皇后問題.png

示例1

輸入:4 輸出:
解法 1: [".Q..", "...Q", "Q...", "..Q."],

解法 2:["..Q.", "Q...", "...Q", ".Q.."]
解釋: 4 皇后問題存在兩個不同的解法观蜗。

問題分析

直接套用《算法之【回溯算法】詳解》中的回溯算法基本步驟以及回溯算法的通用框架模板臊恋。

class Solution:
    def solveNQueens(self, n: int) -> List[List[str]]:
        def backtrack(board, row):
            if row >= n:
                # 此處是將每一行轉(zhuǎn)換為題目要求的字符串形式,如"..Q."代表一行
                cur_res = [''.join(row) for row in board]
                res.append(cur_res)  #保存結(jié)果
                return
            for i in range(n):
                if isValid(row, i, board): #isValid用于判斷該位置是否合法,能夠放置皇后
                    board[row][i] = 'Q'  #放置皇后
                    backtrack(board, row+1) # row+1進入下一行
                    board[row][i] = '.'  #回溯
        res = []
        board = [['.'] * n for _ in range(n)] # 初始化棋盤
        backtrack(board, 0) # row從0開始
        return res

1. 狀態(tài)變量為行row,每次在當前行選擇完一個可以放置的皇后位置后進入下一行row+1;

2. 遞歸結(jié)束條件:當row>=n時墓捻,row0開始抖仅,表示前n-1所有行的皇后均已放置完成,將結(jié)果添加到res中砖第。

if row >= n:
    # 此處是將每一行轉(zhuǎn)換為題目要求的字符串形式,如"..Q."代表一行
    cur_res = [''.join(row) for row in board]
    res.append(cur_res)
    return

3. 可選擇的列表:每一行能夠選擇的位置為0n-1撤卢,但是該位置是否能夠被選擇,需要滿足皇后彼此不能相互攻擊的條件厂画。

此處判斷是否能夠相互攻擊即判斷當前位置(row, col)所在的列凸丸,右斜線方向即左斜線方向是否已經(jīng)有皇后存在拷邢,如果有的話袱院,則當前位置不能再放皇后。

判斷是否有沖突的方式有兩種:

方法一:直接通過循環(huán)判斷

def isVaild(board,row, col):
    #判斷同一列是否沖突
    for i in range(len(board)):
        if board[i][col] == 'Q':
            return False
    # 判斷左上角是否沖突
    i = row -1
    j = col -1
    while i>=0 and j>=0:
        if board[i][j] == 'Q':
            return False
        i -= 1
        j -= 1
    # 判斷右斜線方向是否沖突
    i = row - 1
    j = col + 1
    while i>=0 and j < len(board):
        if board[i][j] == 'Q':
            return False
        i -= 1
        j += 1
    return True

方法二: 左斜線:同一斜線每個點row-col恒定; 右斜線:同一斜線每個點row+col恒定

左斜線為從左上到右下方向瞭稼,同一條斜線上的每個位置滿足行下標與列下標之差相等忽洛,例如 (0,0)和 (3,3)在同一條方向一的斜線上。因此使用行下標與列下標之差即可明確表示每一條方向一的斜線环肘。

左斜.png

右斜線為從右上到左下方向欲虚,同一條斜線上的每個位置滿足行下標與列下標之和相等,例如 (3,0)(3,0) 和 (1,2)(1,2) 在同一條方向二的斜線上悔雹。因此使用行下標與列下標之和即可明確表示每一條方向二的斜線复哆。

右斜.png

下面我們主要使用第二種方法來進行判斷所放置的位置是否合法。

def isValid(row, col):
            # 判斷所放置的位置是否合法
            for i in range(row):
                for j in range(n):
                    # 注:左斜對角線上腌零,同一條斜線上的每個位置滿足行下標與列下標之差相等
                    # 注:右斜對角線上梯找,同一條斜線上的每個位置滿足行下標與列下標之和相等
                    if board[i][j] == 'Q' and (j == col or i + j == row + col or i-j == row-col):
                        return False
            return True

最終代碼

class Solution:
    def solveNQueens(self, n: int) -> List[List[str]]:
        def isValid(row, col):
            # 判斷所放置的位置是否合法
            for i in range(row):
                for j in range(n):
                    if board[i][j] == 'Q' and (j == col or i + j == row + col or i-j == row-col):
                        return False
            return True
        def backtrack(board, row):
            if row >= n:
                cur_res = [''.join(row) for row in board]
                res.append(cur_res)
                return
            for i in range(n):
                if isValid(row, i, board):
                    board[row][i] = 'Q'
                    backtrack(board, row+1)
                    board[row][i] = '.'
        res = []
        board = [['.'] * n for _ in range(n)]
        backtrack(board,0)
        return res

代碼優(yōu)化:通過集合記錄之前放置過元素的正向?qū)蔷€,負向?qū)蔷€益涧,及列锈锤,判斷當前點是否在集合中,在的話說明不滿足要求闲询,這種判斷方式速度更快久免。

def solveNQueens(self, n: int) -> List[List[str]]:
        def isValid(row, col):
            # 如果該點所對應(yīng)的右斜線或左斜線或列已經(jīng)放置了皇后則放回Fasle
            if col in col_hash or (row + col) in pie_hash or (row-col) in na_hash:
                return False
            return True
        def backtrack(board, row):
            if row >= n:
                cur_res = [''.join(row) for row in board]
                res.append(cur_res)
                return
            for col in range(n):
                if isValid(row, col, board):
                    board[row][col] = 'Q'
                    pie_hash.add(row + col)
                    na_hash.add(row - col)
                    col_hash.add(col)
                    backtrack(board, row+1)
                    board[row][col] = '.'
                    pie_hash.remove(row + col)
                    na_hash.remove(row-col)
                    col_hash.remove(col)
        res = []
        board = [['.'] * n for _ in range(n)]
        pie_hash = set() # 記錄放置了皇后的右斜線
        na_hash = set()  # 記錄放置了皇后的左斜線
        col_hash = set() # 記錄放置了皇后的列
        backtrack(board,0)
        return res

后續(xù)會繼續(xù)更新關(guān)于回溯算法的相關(guān)題解,歡迎持續(xù)關(guān)注扭弧!
如果喜歡作者阎姥,歡迎點贊、收藏及關(guān)注鸽捻,謝謝呼巴!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末氨淌,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子伊磺,更是在濱河造成了極大的恐慌盛正,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,946評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件屑埋,死亡現(xiàn)場離奇詭異豪筝,居然都是意外死亡,警方通過查閱死者的電腦和手機摘能,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,336評論 3 399
  • 文/潘曉璐 我一進店門续崖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人团搞,你說我怎么就攤上這事严望。” “怎么了逻恐?”我有些...
    開封第一講書人閱讀 169,716評論 0 364
  • 文/不壞的土叔 我叫張陵像吻,是天一觀的道長。 經(jīng)常有香客問我复隆,道長拨匆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,222評論 1 300
  • 正文 為了忘掉前任挽拂,我火速辦了婚禮惭每,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘亏栈。我一直安慰自己台腥,他們只是感情好,可當我...
    茶點故事閱讀 69,223評論 6 398
  • 文/花漫 我一把揭開白布绒北。 她就那樣靜靜地躺著黎侈,像睡著了一般。 火紅的嫁衣襯著肌膚如雪镇饮。 梳的紋絲不亂的頭發(fā)上蜓竹,一...
    開封第一講書人閱讀 52,807評論 1 314
  • 那天,我揣著相機與錄音储藐,去河邊找鬼俱济。 笑死,一個胖子當著我的面吹牛钙勃,可吹牛的內(nèi)容都是我干的蛛碌。 我是一名探鬼主播,決...
    沈念sama閱讀 41,235評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼辖源,長吁一口氣:“原來是場噩夢啊……” “哼蔚携!你這毒婦竟也來了希太?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,189評論 0 277
  • 序言:老撾萬榮一對情侶失蹤酝蜒,失蹤者是張志新(化名)和其女友劉穎誊辉,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體亡脑,經(jīng)...
    沈念sama閱讀 46,712評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡堕澄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,775評論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了霉咨。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蛙紫。...
    茶點故事閱讀 40,926評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖途戒,靈堂內(nèi)的尸體忽然破棺而出坑傅,到底是詐尸還是另有隱情,我是刑警寧澤喷斋,帶...
    沈念sama閱讀 36,580評論 5 351
  • 正文 年R本政府宣布唁毒,位于F島的核電站,受9級特大地震影響继准,放射性物質(zhì)發(fā)生泄漏枉证。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,259評論 3 336
  • 文/蒙蒙 一移必、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧毡鉴,春花似錦崔泵、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,750評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至陈瘦,卻和暖如春幌甘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背痊项。 一陣腳步聲響...
    開封第一講書人閱讀 33,867評論 1 274
  • 我被黑心中介騙來泰國打工锅风, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鞍泉。 一個月前我還...
    沈念sama閱讀 49,368評論 3 379
  • 正文 我出身青樓皱埠,卻偏偏與公主長得像,于是被迫代替她去往敵國和親咖驮。 傳聞我的和親對象是個殘疾皇子边器,可洞房花燭夜當晚...
    茶點故事閱讀 45,930評論 2 361

推薦閱讀更多精彩內(nèi)容