Leetcode 的強大之處  Swift Code Review 算法題 ( 有效的數獨 , 36 )

Leetcode 的強大之處,挺多的。git

本文寫的是,其強大的討論區。

討論區裏面,有各類具備啓發性的代碼。github

(換句話說,就是有很強的代碼。看了,以爲腦洞大開,大神們把語言的語法特性發揮到了極致)算法

裏面有各類常見語言的實現swift

( 這裏指 Leetcode 主站的, 中文站點的同一功能弱了一點 )數組

進入 Leetcode 的題目,bash

1.png

進入討論區,裏面的討論挺多的,這道題就有 470 個帖子。閉包

本文推薦選擇 "Most Votes",函數

事實就是得分高的代碼,看起來爽性能

2.png

也能夠搜索一下本身關心的, 通常是按語言來搜索。ui

3.png

就 Leetcode 算法題,本文認爲有了 Leetcode 的討論區,和官方題解 (有些沒有,最近的題都有)

其餘的資料...... ,都好像有些弱 (不喜歡英語的少年,除外)


題目描述: 36. 有效的數獨

判斷一個 9x9 的數獨是否有效。只須要根據如下規則,驗證已經填入的數字是否有效便可。

數字 1-9 在每一行只能出現一次。 數字 1-9 在每一列只能出現一次。 數字 1-9 在每個以粗實線分隔的 3x3 宮內只能出現一次。

1

上圖是一個部分填充的有效的數獨。

數獨部分空格內已填入了數字,空白格用 '.' 表示。 示例 :

輸入:

2

輸出: true


題解 ( 改進前):

下面的解法,很是直觀, 根據數獨的成立條件,分三次檢查數字,按行,按列,按塊

(先橫着來,再豎着來,最後一塊一塊來)

由於數獨有數字的惟一性,這裏使用散列集合

每一次處理,經過的狀況分兩種,

掃描一輪,1-9 剛恰好。或者含有 ''.", 其餘的數字各不相同。

class Solution {
    func isValidSudoku(_ board: [[Character]]) -> Bool {
        let count = board.count
        var set = Set<Character>()
        for i in 0..<count{
            //  橫着來,按行,檢查數字
            set = Set(board[i])
            var num = board[i].reduce(0 , {(result : Int, char : Character)
                in
                var cent = 0
                if String(char) == "."{
                    cent = 1
                }
                return result + cent
            })

            //   每一次處理,經過本次循環的狀況分兩種,
            //  掃描一輪,1-9 剛恰好。或者含有 ".", 其餘的數字各不相同。
            //  這裏作了一個針對處理
            if num > 0 , count - num != set.count - 1{
                return false
            }
            else if num == 0, set.count != count{
                return false
            }
            // 豎着來,按列,檢查數字
            set = Set(board.reduce([Character]() , { resultArray, chars in
                return resultArray + [chars[i]]
            }))
            num = board.reduce(0 , {(result : Int, chars : [Character])
                in
                var cent = 0
                if String(chars[i]) == "."{
                    cent = 1
                }
                return result + cent
            })

            if num > 0 , count - num != set.count - 1{
                return false
            }
            else if num == 0, set.count != count{
                return false
            }
            // 一塊一塊來, 按塊,檢查數字
            let characters = board.flatMap{
                return $0
            }
          
            let fisrtMiddle = ( i/3 ) * 27 + ( i % 3 ) * 3 + 1
            let secondMiddle = fisrtMiddle + 9
            let thirdMiddle = fisrtMiddle + 18
            // 找出每一塊
            let arrayThree = [characters[fisrtMiddle - 1], characters[fisrtMiddle], characters[fisrtMiddle + 1],
                            characters[secondMiddle - 1], characters[secondMiddle], characters[secondMiddle + 1],
                           characters[thirdMiddle - 1], characters[thirdMiddle], characters[thirdMiddle + 1]]
            set = Set(arrayThree)
            num = arrayThree.reduce(0 , {(result : Int, char : Character)
                in
                var cent = 0
                if String(char) == "."{
                    cent = 1
                }
                return result + cent
            })

            if num > 0 , count - num != set.count - 1{
                return false
            }
            else if num == 0, set.count != count{
                return false
            }
        }

        return true
    }
}

複製代碼

Code Review :

算法上的提升

不必計算 "." 的個數。先處理數據,把 "." 過濾掉, 再建立散列集合。

按行,按列,按塊查找重複數字,就直觀了不少。不須要考慮 "." 的干擾。

命名要規範,

怎麼知道 set 裏面包含什麼?

不清晰, 不知道 num 是記錄的是什麼的數量。

centarrayThree 是什麼鬼?

改進代碼

if String(char) == "." , 能夠直接寫成 if char == ".」

Swift 中 "." 是字符串的字面量,也是字符的字面量。不須要把字符轉化爲字符串。

改進閉包

按行,計算一次循環,不包含 "." 的

var num = board[i].reduce(0 , {(result : Int, char : Character)
    in
    var cent = 0
    if String(char) == "."{
        cent = 1
    }
    return result + cent
})
複製代碼

1⃣️ 簡寫閉包,用三目,減小臨時變量

var num = board[i].reduce(0 , {(result, char) in
    char == "." ? result + 1 : result
})
複製代碼
這樣處理更高效

先把數據處理乾淨,過濾 "."

let rowDigits = board[i].filter { $0 != "." }
if rowDigits.count != Set(rowDigits).count {
       return false
 }
複製代碼

set = Set(board.reduce([Character]() , { resultArray, chars in
    return resultArray + [chars[i]]
}))
複製代碼

2⃣️ 科學類型轉換

let column = board.map { $0[i]} // Column #i
set = Set(column)
複製代碼

找出每一塊

let fisrtMiddle = ( i/3 ) * 27 + ( i % 3 ) * 3 + 1
            let secondMiddle = fisrtMiddle + 9
            let thirdMiddle = fisrtMiddle + 18
            // 找出每一塊
            let arrayThree = [characters[fisrtMiddle - 1], characters[fisrtMiddle], characters[fisrtMiddle + 1],
                            characters[secondMiddle - 1], characters[secondMiddle], characters[secondMiddle + 1],
                            characters[thirdMiddle - 1], characters[thirdMiddle], characters[thirdMiddle + 1]]

複製代碼

3⃣️ 使用數組片斷 ( slice ), 發揮高階函數的威力

let firstRow = 3 * (i / 3)
let firstCol = 3 * (i % 3)
let block = board[firstRow..<firstRow+3].flatMap { $0[firstCol..<firstCol+3]}

複製代碼

最後的代碼:

class Solution {
    func isValidSudoku(_ board: [[Character]]) -> Bool {

        for i in 0..<9 {
            // 按行,檢查數字
            let rowDigits = board[i].filter { $0 != "." }
            if rowDigits.count != Set(rowDigits).count {
                return false
            }

            // 按列,檢查數字
            let colDigits = board.map { $0[i] }.filter { $0 != "." }
            if colDigits.count != Set(colDigits).count {
                return false
            }

            // 按塊,檢查數字
            let firstRow = 3 * (i / 3)
            let firstCol = 3 * (i % 3)
            let blockDigits = board[firstRow..<firstRow+3].flatMap { $0[firstCol..<firstCol+3]}
                .filter { $0 != "." }
            if blockDigits.count != Set(blockDigits).count {
                return false
            }       
        }

        return true
    }
}
複製代碼

另外一種解法, 更加的函數式,性能差一些

使用 27 個散列集合, 對應 9 次循環 X 3 種方式 ( 按行, 按塊,按列)

排除掉 "." , 有重複的數字,就返回錯誤。

兩層遍歷順利完成後,返回成功。

class Solution {
    func isValidSudoku(_ board: [[Character]]) -> Bool {
        var rowSets = Array(repeating: Set<Character>(), count: 9)
        var colSets = Array(repeating: Set<Character>(), count: 9)
        var blockSets = Array(repeating: Set<Character>(), count: 9)

        for (i, row) in board.enumerated() {
            for (j, char) in row.enumerated() where char != "." {
                if !rowSets[i].insert(char).inserted {
                    return false
                }
                if !colSets[j].insert(char).inserted {
                    return false
                }
                let block = (i / 3) + 3 * (j / 3)
                if !blockSets[block].insert(char).inserted {
                    return false
                }
            }
        }

        return true
    }
}

複製代碼

Leetcode 連接: valid-sudoku

感謝 Martin R 大神 code review 個人代碼

相關代碼: github.com/BoxDengJZ/l…

強大的代碼: Python 實現

相關文章
相關標籤/搜索