ARTS 第19周 | LeetCode 51 N皇后問題 | 如何避免軟件系統的過分設計

ARTS

ARTS 是陳浩(網名左耳朵耗子)在極客時間專欄裏發起的一個活動,目的是經過分享的方式來堅持學習。程序員

每人每週寫一個 ARTS:Algorithm 是一道算法題,Review 是讀一篇英文文章,Technique/Tips 是分享一個小技術,Share 是分享一個觀點。

本週內容

這周的 ARTS 你將看到:面試

  1. N皇后網網紅題.
  2. 什麼是軟件系統過分設計?

Algorithm

本週的算法題是網紅面試題 N皇后問題: LeetCode 51 N-Queens.算法

這是一道很是經典的使用 回溯 + 剪支 的搜索類題目, 這道題雖然是 hard 難度, 但我感受最可貴地方在於想到使用深度優先搜索的方法.編程

下面是代碼, 思路詳見註釋.架構

func solveNQueens(n int) [][]string {

ans := make([][]int, 0)

// 下面是回溯記憶操做, 用於剪支

// 由於每次回溯都按行 row 從上到下進行, 所以同一行的皇后互斥已經在回溯流程被排除了

// 只須要考慮列和兩種對角線

col := make(map[int]struct{}, 0) // 表明列中是否已存在皇后

pie := make(map[int]struct{}, 0) // 表明撇(右上到左下)是否已存在皇后

na := make(map[int]struct{}, 0) // 表明捺(左上到右下)是否已存在皇后

queens := make([]int, 0)

// i 表示搜索進行到第 i 行

var dfs func(i int)

dfs = func(i int) {

if i == n {

// 這裏必須對 queens 進行深拷貝, 不然後續回溯會影響 ans 中的結果

tmp := make([]int, len(queens))

copy(tmp, queens)

ans = append(ans, tmp)

}

for j := 0; j < n; j++ {

_, a := col[j]; _, b := pie[i+j]; _, c := na[i-j]

// 剪支

if !a && !b && !c {

col[j], pie[i+j], na[i-j] = struct{}{}, struct{}{}, struct{}{}

queens = append(queens, j)

dfs(i+1)

delete(col, j); delete(pie, i+j); delete(na, i-j)

// 由於底層數據沒有變, 因此必須刪除本次添加的元素, 後續回溯才能正常使用 queens

queens = queens[:len(queens)-1]

}

}

}

dfs(0)

return genMetric(ans, n)

}

func genMetric(ans [][]int, n int) [][]string {

ret := make([][]string, 0)

s := ""

for i := 0; i < n; i++ {

s += "."

}

for _, queens := range ans {

metric := make([]string, 0)

for _, q := range queens {

l := []byte(s)

l[q] = 'Q'

metric = append(metric, string(l))

}

ret = append(ret, metric)

}

return ret

}

Review 文章推薦

本週的文章推薦是關於軟件架構中的過分設計的: 十個軟件系統過分設計的例子.app

  • 誤區一: 工程師比業務方更聰明.

做者用他十五年的工做經驗擔保, 絕對不存在收斂的業務. 業務這玩意兒只可能愈來愈多, 愈來愈發散. 由於增加就是業務的本質特徵, 不能怪業務方.工具

最好記住一點, 業務方永遠是爸爸.學習

  • 誤區二: 可重用的業務邏輯.

若是隻是單純的經過重用以前的邏輯來構建新的業務邏輯, 即, 在已有的公共邏輯基礎上進行橫向的擴展. 那麼隨着業務功能的增長, 系統會變得愈來愈複雜, 知道不可維護.設計

因此, 在依賴公共邏輯進行功能的橫向擴展以前, 先對公共部分的邏輯作細分(或者叫縱向劃分).code

  • 誤區三: 一切皆泛型.

程序員有時候會被寫出優秀的抽象代碼的願望衝昏了頭腦, 以致於爲了抽象而抽象, 下降了代碼的可讀性卻並無帶來多少可維護性.

有時候, 錯誤的抽象還不如重複的代碼堆疊.

  • 誤區四: 對第三方依賴庫的封裝器功能過於單一.

對依賴庫的封裝必定要考慮更換依賴實現的可能, 同時不要在封裝中摻雜具體的業務邏輯.

  • 誤區五: 盲目使用所謂的高級特性和設計原則並不可取.

給每種對象都抽象除一個接口(或者抽象類), 或者盲目應用某些看似高級的語法特性. 無腦應用那些高級的軟件設計原則, 這樣的作法並不可取. 若是隻是一個很是簡單的小邏輯, 之後複用的可能幾乎沒有, 那麼這種狀況仍是把那些原則和方法忘掉吧. 這些概念不是普通的小工具, 只有場景合適才能發揮做用.

  • 誤區六: 學會同樣技能或者特性就想處處用.

這點有些相似誤區五種的意思, 沒有場景空談使用時沒有意義的.

  • 誤區七: 過於信奉各類 "ity".

做者列出的一些 "ity" 的例子, Configurability, Security, Scalability, Maintainability, Extensibility.

落實這些概念以前, 必定要想一想目前系統的真實場景.

  • 誤區八: 謹慎對待企業內部造輪子.

輪子造出來並非重點, 後期維護的成本也很是高, 決定作這件事情以前必定要想好.

  • 誤區九: 依賴系統現狀卻從不想重構.

重構是必然的結果, 不存在動不了的代碼.

  • 誤區十: 過度高估本身和團隊.

優秀的系統須要優秀的開發者和時間, 並且不管多優秀的開發者的開發速度都是有極限的, 又快又好是不存在的.

Tip 編程技巧

本週沒有技巧...

Share 靈光一閃

理論學習只能提高認知, 只有實踐才能出真知.

相關文章
相關標籤/搜索