Leetcode 的强大之处  算法题解 in Swift ( 有效的数独 , 36 ) 及其 Code Review

栏目: Swift · 发布时间: 7年前

内容简介:讨论区里面,有各种具有启发性的代码。(换句话说,就是有很强的代码。看了,觉得脑洞大开,大神们把语言的语法特性发挥到了极致)里面有各种常见语言的实现

讨论区里面,有各种具有启发性的代码。

(换句话说,就是有很强的代码。看了,觉得脑洞大开,大神们把语言的语法特性发挥到了极致)

里面有各种常见语言的实现

( 这里指 Leetcode 主站的, 中文站点的同一功能弱了一点 )

进入 Leetcode 的题目,

Leetcode 的强大之处  算法题解 in Swift ( 有效的数独 , 36 ) 及其 Code Review

进入讨论区,里面的讨论挺多的,这道题就有 470 个帖子。

本文推荐选择 "Most Votes",

事实就是得分高的代码,看起来爽

Leetcode 的强大之处  算法题解 in Swift ( 有效的数独 , 36 ) 及其 Code Review

也可以搜索一下自己关心的, 一般是按语言来搜索。

Leetcode 的强大之处  算法题解 in Swift ( 有效的数独 , 36 ) 及其 Code Review

就 Leetcode 算法题,本文认为有了 Leetcode 的讨论区,和官方题解 (有些没有,最近的题都有)

其他的资料...... ,都好像有些弱 (不喜欢英语的少年,除外)

题目描述: 36. 有效的数独

判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。

数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。

Leetcode 的强大之处  算法题解 in Swift ( 有效的数独 , 36 ) 及其 Code Review

上图是一个部分填充的有效的数独。

数独部分空格内已填入了数字,空白格用 '.' 表示。 示例 :

输入:

Leetcode 的强大之处  算法题解 in Swift ( 有效的数独 , 36 ) 及其 Code Review

输出: 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 是记录的是什么的数量。

cent 和 arrayThree 是什么鬼?

改进代码

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 实现


以上所述就是小编给大家介绍的《Leetcode 的强大之处  算法题解 in Swift ( 有效的数独 , 36 ) 及其 Code Review》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。

设计原本

设计原本

Frederick P. Brooks, Jr. / InfoQ中文站、王海鹏、高博 / 机械工业出版社 / 2011-1-1 / 55.00元

无论是软件开发、工程还是建筑,有效的设计都是工作的核心。《设计原本:计算机科学巨匠Frederick P. Brooks的思考》将对设计过程进行深入分析,揭示进行有效和优雅设计的方法。 本书包含了多个行业设计者的特别领悟。Frederick P. Brooks, Jr.精确发现了所有设计项目中内在的不变因素,揭示 了进行优秀设计的过程和模式。通过与几十位优秀设计者的对话,以及他自己在几个设计......一起来看看 《设计原本》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

SHA 加密
SHA 加密

SHA 加密工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具