計師筆記:易錯點糾偏與可檢索索引)
簡介《軟件設(shè)計師中級考試知識點整理》以 docx 筆記形式面向軟考軟件設(shè)計師中級考生兼顧需要系統(tǒng)復(fù)盤面向?qū)ο笈c數(shù)據(jù)結(jié)構(gòu)基礎(chǔ)的開發(fā)者可用于考前梳理考點、定位薄弱環(huán)節(jié)。正文圍繞類與對象、繼承、多態(tài)、封裝、抽象化與組合展開逐條解釋單一職責(zé)、開閉、李氏替換、依賴倒置、接口隔離、迪米特和合成復(fù)用七大原則并按創(chuàng)建型、結(jié)構(gòu)型、行為型歸納常見設(shè)計模式同時整理 UML 圖及繼承、組合、關(guān)聯(lián)等關(guān)系樹與二叉樹特性、查找二叉樹、平衡二叉樹、AVL 樹與紅黑樹、排序算法情景對比、線性表和串等模塊目錄層級明確方便按知識點跳轉(zhuǎn)復(fù)習(xí)。資源包內(nèi)含 1 個 docx 文件約 31.83MB已有 894 人學(xué)習(xí)。整體偏提綱式梳理對易混淆的設(shè)計原則、模式分類和樹結(jié)構(gòu)特性可作對照記憶適合沖刺階段配合真題逐項核對快速建立知識框架。1. 一份目錄先給出答案筆記的價值不在厚度在能不能定位打開目錄會先愣一下前面連續(xù)七八個標(biāo)題共用第 4 頁中間十幾個標(biāo)題擠在 9、10 頁整份目錄最后停在 40 多頁。這說明它是按主題塊排的不是按頁排的。對備考軟考中級軟件設(shè)計師的人來說這恰好是個信號——一份知識點整理能不能用不看排版看它有沒有把面向?qū)ο蟆?shù)據(jù)結(jié)構(gòu)、算法、軟件工程、網(wǎng)絡(luò)、數(shù)據(jù)庫這幾條主線串成可檢索的骨架。這份 docx 覆蓋面基本貼著大綱走適合已有基礎(chǔ)、需要快速回歸和查漏的人零基礎(chǔ)拿它當(dāng)唯一教材會讀得很吃力因為很多條目只有結(jié)論沒有推導(dǎo)。2. 面向?qū)ο笈c設(shè)計模式7 大原則在題干里怎么被認(rèn)出來面向?qū)ο筮@部分在下午題里出得不算多但上午題偏愛兩種問法給一段場景問體現(xiàn)了哪條原則給一個類圖問屬于哪類模式。筆記把類、對象、繼承、多態(tài)、封裝、抽象、組合這七個基本概念放在最前面緊接著進(jìn)七大原則這個編排順序是對的——原則本身就是概念的組合應(yīng)用先背概念再背原則等于把同一件事記兩遍。2.1 七大原則的判定順序與題干信號詞同時出現(xiàn)多個信號時我一般按這個優(yōu)先級往下篩單一職責(zé) → 開閉 → 里氏替換 → 依賴倒置 → 接口隔離 → 迪米特 → 合成復(fù)用。排在前面的更結(jié)構(gòu)性出題人寫場景時往往就是沖著它去的排在后面的更實現(xiàn)性多數(shù)時候是干擾項。原則一句話內(nèi)核題干高頻信號詞典型反例單一職責(zé) SRP一個類只有一個引起變化的原因該類同時負(fù)責(zé)……和……一個類既管連接池又管報表渲染開閉 OCP對擴展開放對修改關(guān)閉新增一種……時不修改原有代碼加一個支付渠道就要動 if-else里氏替換 LSP子類能替換父類且行為不變子類重寫后父類原有調(diào)用出錯子類強化入?yún)⑿r?、拋父類未聲明的異常依賴倒?DIP高層與低層都依賴抽象高層模塊不應(yīng)依賴低層實現(xiàn)業(yè)務(wù)類里直接 new 具體驅(qū)動接口隔離 ISP接口拆細(xì)不強迫依賴無用方法接口中存在大量空實現(xiàn)一個胖接口塞二十個方法迪米特 LoD只與直接朋友通信調(diào)用鏈過長中間類轉(zhuǎn)手a.getB().getC().doWork()合成復(fù)用 CRP優(yōu)先組合而非繼承繼承層次過深難以維護(hù)為復(fù)用兩個方法繼承整個基類最容易混的兩組要單獨拎出來。第一組是里氏替換和依賴倒置里氏管的是子類型的行為契約判斷依據(jù)是替換后調(diào)用方有沒有感知到差異依賴倒置管的是依賴的方向判斷依據(jù)是代碼里有沒有出現(xiàn)具體類名。第二組是單一職責(zé)和接口隔離單一職責(zé)約束的是類接口隔離約束的是接口前者看變化原因有幾個后者看客戶端被迫實現(xiàn)了幾個用不上的方法。提示題干里出現(xiàn)子類不能替代父類時別急著選里氏替換。如果場景描述的是父類里塞了子類用不到的方法答案通常是接口隔離因為問題出在接口粒度而不是繼承契約。2.2 三種模式分類的記憶錨點原文對創(chuàng)建型、結(jié)構(gòu)型、行為型的描述用了工廠模式、抽象工廠模式、builder 模式這種中英混寫讀的時候要注意統(tǒng)一成中文名否則考場上看到建造者模式會反應(yīng)不過來。分類數(shù)量成員一句話錨點創(chuàng)建型5工廠方法、抽象工廠、建造者、原型、單例都在解決對象怎么造出來結(jié)構(gòu)型7適配器、橋接、組合、裝飾器、外觀、享元、代理都在解決對象怎么拼起來行為型11職責(zé)鏈、命令、解釋器、迭代器、中介者、備忘錄、觀察者、狀態(tài)、策略、模板方法、訪問者都在解決職責(zé)怎么分、消息怎么傳按造、拼、分三個動詞記比按字母順序背可靠得多。享元是最容易被歸錯的一個很多人一看共享對象狀態(tài)就往行為型放其實它處理的是對象的結(jié)構(gòu)復(fù)用屬于結(jié)構(gòu)型。訪問者放行為型也是同理——它變的是對元素的操作不是元素本身的結(jié)構(gòu)。2.3 用一段代碼把開閉與依賴倒置跑出來原則背得再熟不如自己寫一遍反例再改一遍。下面這段是加一個折扣類型就要改結(jié)算類的典型翻車寫法改造成策略之后兩個原則同時成立。from abc import ABC, abstractmethod class Discount(ABC): 折扣抽象高層只認(rèn)這個接口不認(rèn)具體實現(xiàn) abstractmethod def apply(self, amount: float) - float: ... class NoDiscount(Discount): def apply(self, amount: float) - float: return amount class PercentDiscount(Discount): def __init__(self, rate: float): self.rate rate # 0.9 表示九折1.0 表示原價 def apply(self, amount: float) - float: return amount * self.rate class Checkout: def __init__(self, strategy: Discount): self._strategy strategy # 構(gòu)造注入依賴的是抽象 def total(self, amount: float) - float: return self._strategy.apply(amount) if __name__ __main__: print(Checkout(NoDiscount()).total(100.0)) # 100.0 print(Checkout(PercentDiscount(0.9)).total(100.0)) # 90.0邏輯說明Checkout里沒有任何if 類型 ...的分支新增滿減階梯價只需要再寫一個Discount子類Checkout一行不動這就是開閉原則的落地形態(tài)。Checkout.__init__的形參類型標(biāo)注是Discount而不是PercentDiscount高層模塊依賴抽象而非具體實現(xiàn)這就是依賴倒置。參數(shù)上要注意rate的取值范圍用小數(shù)表示若改成百分?jǐn)?shù)90 表示九折必須在文檔里寫清楚否則調(diào)用方傳 0.9 和傳 90 會得到完全不同的結(jié)果這類單位不一致在下午題里也常作為陷阱出現(xiàn)。再補一個反向驗證把PercentDiscount.apply改成先校驗amount 0負(fù)數(shù)直接拋異常。此時用子類替換父類的調(diào)用方原本傳 0 不報錯的代碼開始報錯行為契約被破壞這就是里氏替換的違反。自己動手改一遍比看十遍定義管用。3. UML 關(guān)系與樹結(jié)構(gòu)兩種層次感考點的交叉審法UML 和數(shù)據(jù)結(jié)構(gòu)在筆記里是挨著放的看似不相關(guān)其實考的是同一種能力——判斷層次和強弱。UML 關(guān)系判斷的是類與類之間的耦合強度樹結(jié)構(gòu)判斷的是結(jié)點之間的層級約束。把這兩塊放在一起復(fù)習(xí)腦子里建立的是一套關(guān)系模型而不是兩堆散點。3.1 UML 關(guān)系的強度階梯原文只列了繼承、組合、關(guān)聯(lián)三種實際考綱要求區(qū)分六種缺了聚合、依賴和實現(xiàn)。按耦合從弱到強排成一條線判斷時從弱往強推比死記符號穩(wěn)。關(guān)系圖形符號耦合強度生命周期代碼里的常見形態(tài)依賴虛線箭頭最弱無關(guān)方法參數(shù)、局部變量、返回值關(guān)聯(lián)實線可帶箭頭弱各自獨立成員變量持有對方引用聚合空心菱形中部分可獨立存在成員變量構(gòu)造時傳入組合實心菱形強部分隨整體銷毀成員變量內(nèi)部 new 出來泛化實線空心三角強繼承class B extends A實現(xiàn)虛線空心三角強契約class B implements I聚合和組合的區(qū)分只看一句整體銷毀時部分還能不能活。學(xué)校和學(xué)生是聚合學(xué)校撤銷了學(xué)生還在訂單和訂單明細(xì)是組合訂單刪了明細(xì)沒有獨立意義。圖上的菱形是空是實直接對應(yīng)這個語義記混的時候回到這句話就行。泛化與實現(xiàn)的區(qū)分也常被問。泛化是is-a且?guī)崿F(xiàn)繼承子類直接拿到父類的方法體實現(xiàn)只是can-do接口里沒有方法體子類必須自己寫。題干里出現(xiàn)抽象類和接口二選一時看有沒有共享的默認(rèn)實現(xiàn)。3.2 二叉樹性質(zhì)與查找樹、平衡樹的判定樹這塊筆記里有個表述要注意原文寫二叉樹的重要特性左子樹和右子樹的高度不能超過 1。這句話描述的是平衡二叉樹AVL的約束不是普通二叉樹的特性。普通二叉樹只有下面這幾條硬性質(zhì)。性質(zhì)表達(dá)式適用對象第 i 層最多結(jié)點數(shù)2^(i-1)任意二叉樹深度為 k 最多結(jié)點數(shù)2^k - 1任意二叉樹葉子與度為 2 的結(jié)點關(guān)系n0 n2 1任意二叉樹n 個結(jié)點的完全二叉樹深度?log2 n? 1完全二叉樹編號 i 的左右孩子、父結(jié)點2i、2i1、?i/2?順序存儲的完全二叉樹左右子樹高度差絕對值 ≤ 1平衡二叉樹n0 n2 1這條建議用代碼驗證一遍比背公式記得住也順便把完全二叉樹的編號規(guī)則過一遍。import math def walk_complete_tree(n: int): 按完全二叉樹的順序編號規(guī)則統(tǒng)計各類結(jié)點 n0 n1 n2 0 for i in range(1, n 1): children [c for c in (2 * i, 2 * i 1) if c n] if len(children) 0: n0 1 elif len(children) 1: n1 1 else: n2 1 return n0, n1, n2 for total in (10, 15, 31): n0, n1, n2 walk_complete_tree(total) depth math.floor(math.log2(total)) 1 print(fn{total} 深度{depth} 葉子{n0} 單分支{n1} 雙分支{n2} 校驗n0n21 - {n0 n2 1})邏輯說明2*i和2*i1是完全二叉樹順序存儲的編號規(guī)則超出n就說明該孩子不存在據(jù)此可以反推每個結(jié)點的度數(shù)。math.floor(math.log2(total)) 1對應(yīng)深度公式注意total為 1 時結(jié)果是 1邊界正確。運行結(jié)果里n10時葉子 5 個、雙分支 4 個5 4 1成立。參數(shù)上唯一要小心的是n必須是正整數(shù)傳 0 會導(dǎo)致log2報錯。查找二叉樹BST的判定標(biāo)準(zhǔn)只有一條好用的中序遍歷序列單調(diào)遞增。平衡二叉樹則看每個結(jié)點的平衡因子左子樹高度減右子樹高度絕對值不超過 1。紅黑樹屬于近似平衡插入刪除時旋轉(zhuǎn)次數(shù)比 AVL 少適合寫操作頻繁的場景堆是完全二叉樹 堆序只保證父結(jié)點與子結(jié)點的大小關(guān)系兄弟之間無序這一點經(jīng)常被拿來出判斷題。3.3 排序?qū)Ρ缺硪B穩(wěn)定性一起背各種排序算法的復(fù)雜度表誰都會背出錯多半出在穩(wěn)定性那一列。算法平均最壞最好輔助空間穩(wěn)定性冒泡O(n2)O(n2)O(n)O(1)穩(wěn)定插入O(n2)O(n2)O(n)O(1)穩(wěn)定選擇O(n2)O(n2)O(n2)O(1)不穩(wěn)定希爾O(n^1.3)O(n2)O(n)O(1)不穩(wěn)定快速O(n log n)O(n2)O(n log n)O(log n)不穩(wěn)定歸并O(n log n)O(n log n)O(n log n)O(n)穩(wěn)定堆O(n log n)O(n log n)O(n log n)O(1)不穩(wěn)定基數(shù)O(d(nr))O(d(nr))O(d(nr))O(nr)穩(wěn)定穩(wěn)定的四個是冒泡、插入、歸并、基數(shù)其余都不穩(wěn)定。這個結(jié)論光背容易記反跑一遍記錄對key, tag的排序看相同 key 的相對次序有沒有變一分鐘就能確認(rèn)。def selection_sort_records(records): a records[:] n len(a) for i in range(n - 1): m i for j in range(i 1, n): if a[j][0] a[m][0]: # 只比較 key忽略 tag m j a[i], a[m] a[m], a[i] # 遠(yuǎn)距離交換會打亂相等元素的相對次序 return a def bubble_sort_records(records): a records[:] n len(a) for i in range(n - 1): swapped False for j in range(n - 1 - i): if a[j][0] a[j 1][0]: # 嚴(yán)格大于才交換相等時保持原位 a[j], a[j 1] a[j 1], a[j] swapped True if not swapped: # 一趟無交換最好情況 O(n) break return a data [(2, a), (2, b), (1, c)] print(selection_sort_records(data)) # [(1,c), (2,b), (2,a)] a、b 次序被換不穩(wěn)定 print(bubble_sort_records(data)) # [(1,c), (2,a), (2,b)] a、b 次序保留穩(wěn)定邏輯說明把每個元素寫成(排序鍵, 標(biāo)記)的二元組排序后只看標(biāo)記字母的順序就能判斷穩(wěn)定性。選擇排序不穩(wěn)定問題出在最小值與首位遠(yuǎn)距離交換這一步它會把相等元素中靠前的那個甩到后面去。冒泡排序的交換條件是嚴(yán)格大于相等時不動作所以穩(wěn)定。參數(shù)上records必須是可切片復(fù)制的序列函數(shù)內(nèi)部做了[:]拷貝不會污染原列表。4. 算法與開發(fā)模型復(fù)雜度估算、模型選型的落地判斷算法部分的難點不在寫代碼在給一個場景判斷該用哪種思想。軟件工程部分的難點不在背模型名字在給一個項目特征判斷該選哪個模型。這兩類題本質(zhì)上是同一種從特征反推方法。4.1 分治、回溯、貪心、動態(tài)規(guī)劃怎么區(qū)分算法思想成立條件典型題目判斷關(guān)鍵句分治子問題獨立、可合并二分查找、歸并排序、快速排序?qū)栴}分成規(guī)模更小的同類子問題回溯解空間可枚舉、能剪枝N 皇后、圖著色、迷宮求解嘗試、失敗則回退貪心每步局部最優(yōu)可推出全局最優(yōu)部分背包、哈夫曼編碼、Prim、Kruskal每次選當(dāng)前最好的動態(tài)規(guī)劃最優(yōu)子結(jié)構(gòu) 重疊子問題0-1 背包、最長公共子序列、矩陣連乘子問題被重復(fù)計算被問得最多的對比是 0-1 背包和部分背包。物品不可分割時只能用動態(tài)規(guī)劃因為貪心會翻車容量 10物品 A 重 6 值 10、B 重 5 值 9、C 重 5 值 9按單位價值貪心先拿 A剩下容量 4 什么也裝不下總價值 10最優(yōu)解是拿 B 和 C總價值 18。物品可分割時貪心就是最優(yōu)的因為剩余容量能裝小數(shù)部分?;厮莺蛣討B(tài)規(guī)劃的邊界也?;臁;厮菔亲叩降自倩赝藭L問同一個狀態(tài)很多次動態(tài)規(guī)劃是把中間結(jié)果存起來同一狀態(tài)只算一次。題干里出現(xiàn)記憶化填表狀態(tài)轉(zhuǎn)移方程基本鎖定動態(tài)規(guī)劃出現(xiàn)剪枝深度優(yōu)先遍歷解空間樹鎖定回溯。4.2 復(fù)雜度用實測驗證別只背表O(n2) 和 O(n log n) 的分界點在哪里光看公式?jīng)]感覺。寫個小腳本統(tǒng)計比較次數(shù)拿比值看增長趨勢比硬背結(jié)論直觀得多。import math, random def bubble_compare_count(arr): a arr[:] n len(a) cnt 0 for i in range(n - 1): swapped False for j in range(n - 1 - i): cnt 1 # 每比較一次計數(shù)一次 if a[j] a[j 1]: a[j], a[j 1] a[j 1], a[j] swapped True if not swapped: break return cnt random.seed(7) for n in (32, 64, 128, 256, 512): data [random.random() for _ in range(n)] # 隨機序列避開有序觸發(fā)提前退出 c bubble_compare_count(data) print(fn{n:4d} 比較次數(shù){c:7d} c/(n*logn){c / (n * math.log2(n)):.2f})邏輯說明隨機輸入下冒泡排序不會提前退出比較次數(shù)接近n(n-1)/2。把結(jié)果除以n log n如果比值隨 n 線性增長說明分子是 n2 量級。這個方法的通用價值在于考場上一時想不起某個算法的時間復(fù)雜度用規(guī)模翻倍、耗時變幾倍的思路也能推出來——翻倍后耗時變 4 倍是 O(n2)變 2 倍多一點的通常是 O(n log n)。參數(shù)上random.seed固定隨機源保證多次運行結(jié)果一致方便對照。常見算法的時間復(fù)雜度結(jié)論整理成一句話二分查找 O(log n)、歸并排序 O(n log n)、快速排序平均 O(n log n) 最壞 O(n2)、堆排序 O(n log n)、斐波那契遞歸樸素實現(xiàn) O(2^n)、矩陣連乘動態(tài)規(guī)劃 O(n3)。4.3 開發(fā)模型選型對照筆記里排了瀑布、原型、增量、螺旋、V 模型、噴泉、敏捷七種容易的問題是知道名字但選不出來。模型適用場景關(guān)鍵特征主要風(fēng)險瀑布需求明確且穩(wěn)定階段嚴(yán)格串行、文檔驅(qū)動后期變更代價大原型需求模糊、需快速確認(rèn)先做可運行樣品原型被當(dāng)成成品交付增量可分批交付、核心先行按功能塊逐個發(fā)布需要良好的架構(gòu)規(guī)劃螺旋大型高風(fēng)險項目每輪含風(fēng)險分析成本高、管理復(fù)雜V 模型強測試要求的嵌入類項目開發(fā)與測試階段一一對應(yīng)靈活性差噴泉面向?qū)ο箝_發(fā)各階段無縫迭代、可回溯缺乏明確里程碑敏捷需求頻繁變化的小團(tuán)隊短迭代、可工作軟件優(yōu)先文檔可能不足XP 作為敏捷的具體實踐考點集中在四條結(jié)對編程、測試先行、小型發(fā)布、持續(xù)重構(gòu)。題干里出現(xiàn)兩人共用一臺機器先寫測試再寫實現(xiàn)每兩周發(fā)布一次指向的都是 XP 而不是泛泛的敏捷。螺旋模型和增量模型也常被混區(qū)分點在于螺旋每輪必須做風(fēng)險分析增量只是分批交付。4.4 關(guān)鍵路徑正推逆推和總時差關(guān)鍵路徑是上午題的??褪止に阋槐椴浑y但活動一多就容易算錯。規(guī)則只有兩條正推求最早開始時間取所有前置活動最早完成時間的最大值逆推求最晚開始時間取所有后置活動最晚開始時間的最小值。總時差 最晚開始 - 最早開始為 0 的活動連起來就是關(guān)鍵路徑。def critical_path(acts): acts: {活動名: (工期, [前置活動])} ES, EF {}, {} def forward(name): if name in EF: return dur, preds acts[name] for p in preds: forward(p) ES[name] max((EF[p] for p in preds), default0) EF[name] ES[name] dur for k in acts: forward(k) total max(EF.values()) succ {k: [] for k in acts} for k, (_, preds) in acts.items(): for p in preds: succ[p].append(k) LS, LF {}, {} def backward(name): if name in LS: return for s in succ[name]: backward(s) LF[name] min((LS[s] for s in succ[name]), defaulttotal) LS[name] LF[name] - acts[name][0] for k in acts: backward(k) slack {k: LS[k] - ES[k] for k in acts} path [k for k in acts if slack[k] 0] return total, slack, path acts { A: (3, []), # 需求梳理 B: (4, [A]), # 方案設(shè)計 C: (2, [A]), # 環(huán)境準(zhǔn)備 D: (5, [B, C]), # 核心開發(fā) E: (2, [C]), # 接口聯(lián)調(diào) F: (3, [D, E]), # 驗收測試 } print(critical_path(acts)) # (15, {A:0,B:0,C:2,D:0,E:5,F:0}, [A,B,D,F])邏輯說明forward用遞歸保證前置活動先算default0處理沒有前置的起始活動backward反向遞歸終點活動的LF取總工期。返回的slack是每個活動的總時差為 0 說明一天都拖不得。參數(shù)上活動名必須唯一前置列表里的名字必須已定義否則forward會拋KeyError——手工畫圖時漏掉某個前置活動算出來的工期會偏短這是最常見的錯誤。5. 避坑與常見問題筆記里最容易背反的 5 個點這份筆記整體結(jié)構(gòu)清楚但有幾處表述和筆誤會讓人在復(fù)習(xí)時繞進(jìn)去我自己就翻過車。下面五條按現(xiàn)象 → 原因 → 解決記錄都是我實際遇到過的問題。5.1 按目錄頁碼跳轉(zhuǎn)找不到內(nèi)容現(xiàn)象照目錄上的頁碼去翻連續(xù)七八個標(biāo)題都指向同一頁翻過去發(fā)現(xiàn)那一頁擠了十來個知識點實際內(nèi)容排在后面幾頁。原因這份目錄是 Word 自動生成后整段粘貼的域沒有更新頁碼停留在上一次排版的狀態(tài)而且原始筆記本身是按主題塊組織不是按頁分。解決不要用頁碼定位用標(biāo)題層級定位。如果需要在電子版里檢索先把它轉(zhuǎn)成帶層級標(biāo)記的文本下一章的腳本就是干這個的再用關(guān)鍵詞搜索比翻頁碼可靠。5.2 把平衡二叉樹的高度約束當(dāng)成普通二叉樹性質(zhì)現(xiàn)象原文寫二叉樹的重要特性左子樹和右子樹的高度不能超過 1照著背遇到普通二叉樹的題目也套這句判斷直接錯。原因這句話是 AVL 樹的平衡條件被錯寫到了二叉樹概述里。普通二叉樹對左右子樹高度沒有任何限制可以是一條鏈。解決三條硬性質(zhì)記住——第 i 層最多 2^(i-1) 個結(jié)點、深度 k 最多 2^k-1 個結(jié)點、n0 n2 1。高度差絕對值不超過 1 只屬于平衡二叉樹做題時看到平衡兩個字才啟用這條。5.3 關(guān)鍵詞檢索不到因為原文有筆誤現(xiàn)象想在文檔里搜里氏替換搜不到搜復(fù)雜度也搜不到幾處。原因原文寫的是李氏替換原則和McCabe 復(fù)制度還有網(wǎng)絡(luò)完全控制技術(shù)應(yīng)為網(wǎng)絡(luò)安全控制技術(shù)。這類輸入法誤寫在整理型筆記里很常見它不影響閱讀但會讓全文檢索失效。解決檢索時做同義詞展開。里氏/李氏、復(fù)雜度/復(fù)制度、網(wǎng)絡(luò)安全/網(wǎng)絡(luò)完全、建造者/builder四個詞對都記一下。熟手重建索引時可以在腳本里加一層別名映射把常見誤寫一起收進(jìn)去。5.4 排序算法的穩(wěn)定性記反現(xiàn)象把快速排序、堆排序、選擇排序、希爾排序記成穩(wěn)定的選擇題一對答案全錯。原因只盯著復(fù)雜度那一列背穩(wěn)定性那一列靠印象補而印象里快排很快所以應(yīng)該穩(wěn)定這種直覺是錯的。解決只記穩(wěn)定的四個——冒泡、插入、歸并、基數(shù)其余的默認(rèn)不穩(wěn)定遇到拿不準(zhǔn)的用上一章那份(key, tag)腳本跑一遍。別用直覺推這是純記憶題。5.5 設(shè)計模式歸類張冠李戴現(xiàn)象把建造者放進(jìn)結(jié)構(gòu)型把享元放進(jìn)行為型答題時二選一必錯。原因建造者原文寫的是英文 builder讀的時候沒和中文名對應(yīng)上享元的共享字樣容易被理解成對象間的交互行為。解決按造、拼、分三個動詞重新分組創(chuàng)建型 5 個解決對象怎么造結(jié)構(gòu)型 7 個解決對象怎么拼行為型 11 個解決職責(zé)怎么分。享元歸結(jié)構(gòu)型因為它處理的是對象內(nèi)部狀態(tài)的共享結(jié)構(gòu)不涉及對象之間的消息傳遞。6. 把 docx 變成可檢索索引我用 python-docx 做的一次改造純靠 Word 的查找功能翻一份四十多頁的筆記效率很低尤其是想同時看某個知識點在哪些章節(jié)出現(xiàn)過的時候。我一般的做法是先用python-docx把段落和樣式層級導(dǎo)出來生成一份帶標(biāo)題標(biāo)記的 Markdown 副本之后所有檢索都在副本上做。from docx import Document doc Document(軟件設(shè)計師中級考試知識點整理.docx) lines [] for p in doc.paragraphs: text p.text.strip() if not text: continue style p.style.name # Heading 1 / Heading 2 / Normal if style.startswith(Heading 1): lines.append(f## {text}) elif style.startswith(Heading 2): lines.append(f### {text}) else: lines.append(text) with open(軟考筆記_索引.md, w, encodingutf-8) as f: f.write(\n\n.join(lines)) print(f導(dǎo)出段落 {len(lines)} 條)邏輯說明p.style.name返回的是樣式名Heading 1對應(yīng)章、Heading 2對應(yīng)節(jié)據(jù)此還原出兩級標(biāo)題結(jié)構(gòu)正文段落原樣保留。參數(shù)上要注意兩點一是原始文檔如果用的是自定義樣式名比如標(biāo)題 1startswith的判斷要跟著改二是導(dǎo)出用utf-8編碼別用系統(tǒng)默認(rèn)否則在部分終端里打開是亂碼。生成的 Markdown 副本只用于檢索不用于閱讀排版所以段落之間的空行用\n\n簡單拼接就夠了。有了副本之后檢索就變成一行命令的事# 在所有標(biāo)題和正文里找關(guān)鍵詞帶行號輸出方便回原文定位 rg -n McCabe|環(huán)路|復(fù)雜度|復(fù)制度 軟考筆記_索引.md # 只看標(biāo)題層級快速確認(rèn)某個知識點歸屬哪一章 rg -n ^#{2,3} 軟考筆記_索引.md | rg 測試|維護(hù)|CMMI第二條命令里^#{2,3}匹配二級和三級標(biāo)題管道再過濾一次幾秒鐘就能看出軟件測試相關(guān)的小節(jié)分布在哪幾個章下面比在 Word 導(dǎo)航窗格里一個個展開快得多。從那以后我每次拿到這類整理型筆記都強制先跑一遍導(dǎo)出腳本再做復(fù)習(xí)計劃——不是為了排版好看是因為只有把結(jié)構(gòu)攤平成可檢索的文本才能發(fā)現(xiàn)原文里那些頁碼錯位、術(shù)語筆誤和歸類錯誤。這些東西不攤開看考場上就會變成實打?qū)嵉氖Х贮c。希望幫到你。本文還有配套的精品資源點擊獲取