據(jù)結(jié)構(gòu)學(xué)習(xí)必備:從指針遞歸到復(fù)雜度分析的四步預(yù)備知識(shí)框架)
1. 為什么我們需要“數(shù)據(jù)結(jié)構(gòu)預(yù)備知識(shí)”這個(gè)模板如果你正準(zhǔn)備開(kāi)始學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)或者已經(jīng)學(xué)了一段時(shí)間但感覺(jué)知識(shí)體系像一盤散沙那么你很可能需要一個(gè)“預(yù)備知識(shí)模板”。這不是一個(gè)具體的代碼文件而是一個(gè)認(rèn)知框架一份學(xué)習(xí)地圖。我見(jiàn)過(guò)太多初學(xué)者一上來(lái)就抱著《算法導(dǎo)論》啃紅黑樹結(jié)果被指針、遞歸、內(nèi)存模型這些前置概念卡得寸步難行信心大受打擊最后得出結(jié)論“我可能不適合編程”。這太可惜了。實(shí)際上數(shù)據(jù)結(jié)構(gòu)的學(xué)習(xí)路徑是有清晰依賴關(guān)系的。就像蓋房子你得先打地基、砌墻最后才能裝修。數(shù)據(jù)結(jié)構(gòu)的地基就是那些看似基礎(chǔ)卻決定了你上層建筑能蓋多高的“預(yù)備知識(shí)”。這個(gè)“模板”要解決的就是幫你把這些散落的知識(shí)點(diǎn)按照正確的順序和邏輯串聯(lián)起來(lái)形成一個(gè)穩(wěn)固的支撐體系。它讓你知道在學(xué)習(xí)“鏈表”之前你必須先掌握“指針”和“動(dòng)態(tài)內(nèi)存”在學(xué)習(xí)“樹”之前你必須先吃透“遞歸”和“結(jié)構(gòu)體”。這份模板的價(jià)值在于它能幫你節(jié)省大量在黑暗中摸索的時(shí)間讓你每一步都踩在堅(jiān)實(shí)的臺(tái)階上而不是在概念的流沙里掙扎。2. 核心預(yù)備知識(shí)模塊拆解你的四塊基石一個(gè)完整的數(shù)據(jù)結(jié)構(gòu)學(xué)習(xí)旅程需要建立在四塊核心基石之上。缺了任何一塊你后續(xù)的學(xué)習(xí)都會(huì)搖搖晃晃。2.1 編程語(yǔ)言基礎(chǔ)不只是語(yǔ)法更是思想很多人誤以為學(xué)數(shù)據(jù)結(jié)構(gòu)就是學(xué)C語(yǔ)言或C的語(yǔ)法。錯(cuò)了。語(yǔ)言是載體核心是背后的編程思想。你需要掌握的不是“for循環(huán)怎么寫”而是“如何用循環(huán)遍歷一個(gè)數(shù)據(jù)集合”。具體來(lái)說(shuō)你需要精通以下幾點(diǎn)變量與數(shù)據(jù)類型深刻理解基本類型int,float,char和復(fù)合類型數(shù)組、結(jié)構(gòu)體在內(nèi)存中的存儲(chǔ)方式。比如一個(gè)int占4個(gè)字節(jié)一個(gè)int數(shù)組在內(nèi)存中是連續(xù)存放的。這個(gè)概念是理解數(shù)組隨機(jī)訪問(wèn)效率高的基礎(chǔ)。指針與引用這是數(shù)據(jù)結(jié)構(gòu)的靈魂尤其是對(duì)于C/C學(xué)習(xí)者。你必須搞清楚什么是指針變量它存儲(chǔ)的是什么一個(gè)內(nèi)存地址指針的運(yùn)算p意味著什么指針與數(shù)組的關(guān)系數(shù)組名在多數(shù)情況下可以看作指向首元素的常量指針。二級(jí)指針指向指針的指針在復(fù)雜數(shù)據(jù)結(jié)構(gòu)如鏈表的頭指針處理中非常常見(jiàn)。對(duì)于Java/Python學(xué)習(xí)者雖然不直接操作指針但必須理解“引用”的概念。變量名指向一個(gè)對(duì)象賦值操作是復(fù)制引用而非對(duì)象本身這是理解鏈表、樹等結(jié)構(gòu)的關(guān)鍵。函數(shù)與參數(shù)傳遞重點(diǎn)理解值傳遞、指針傳遞C/C和引用傳遞C的區(qū)別。當(dāng)你寫一個(gè)函數(shù)來(lái)修改鏈表節(jié)點(diǎn)時(shí)為什么有時(shí)需要傳入指向指針的指針因?yàn)槟阈枰薷恼{(diào)用者手里的那個(gè)指針本身而不僅僅是它指向的內(nèi)容。結(jié)構(gòu)體/類這是封裝數(shù)據(jù)的容器。學(xué)習(xí)如何用struct或class定義一個(gè)“節(jié)點(diǎn)”它包含數(shù)據(jù)域和指針域。這是構(gòu)建鏈表、樹、圖等非連續(xù)存儲(chǔ)結(jié)構(gòu)的磚塊。內(nèi)存管理malloc/freeCnew/deleteC 或垃圾回收機(jī)制Java/Python。你必須清楚動(dòng)態(tài)申請(qǐng)的內(nèi)存來(lái)自“堆”需要手動(dòng)管理生命周期否則會(huì)導(dǎo)致內(nèi)存泄漏申請(qǐng)了不釋放或野指針釋放了還繼續(xù)用。注意不要試圖一次性精通所有語(yǔ)言特性。圍繞數(shù)據(jù)結(jié)構(gòu)的需要來(lái)學(xué)習(xí)。例如學(xué)習(xí)“鏈表”時(shí)就專注于結(jié)構(gòu)體和指針學(xué)習(xí)“?!睍r(shí)再研究一下函數(shù)調(diào)用棧幀。2.2 數(shù)學(xué)與邏輯基礎(chǔ)算法的尺子數(shù)據(jù)結(jié)構(gòu)與算法密不可分而算法分析離不開(kāi)簡(jiǎn)單的數(shù)學(xué)工具。你不需要高深的數(shù)學(xué)但下面這些概念必須成為本能時(shí)間復(fù)雜度與空間復(fù)雜度這是衡量算法效率的標(biāo)尺。你必須會(huì)看、會(huì)算。大O表示法理解O(1), O(n), O(log n), O(n2)分別代表什么性能級(jí)別。能分析簡(jiǎn)單程序段的時(shí)間復(fù)雜度例如一個(gè)嵌套循環(huán)通常是O(n2)。常見(jiàn)復(fù)雜度對(duì)比O(1) O(log n) O(n) O(n log n) O(n2) O(2^n)。要能直觀地感受到當(dāng)n很大時(shí)O(n2)的算法比O(n log n)的算法慢得多。空間復(fù)雜度算法運(yùn)行所需額外內(nèi)存的度量。遞歸調(diào)用會(huì)消耗??臻g深度過(guò)大可能導(dǎo)致棧溢出。遞歸這是理解樹、圖相關(guān)算法如遍歷、回溯、分治的鑰匙。很多人怕遞歸其實(shí)關(guān)鍵在于理解“遞歸三要素”終止條件什么情況下函數(shù)直接返回不再調(diào)用自身。遞歸調(diào)用函數(shù)如何調(diào)用自身但參數(shù)規(guī)模必須減小向終止條件靠近。返回與合并如何將子問(wèn)題的結(jié)果合并得到當(dāng)前問(wèn)題的解。實(shí)操心得初學(xué)遞歸時(shí)不要試圖在大腦里展開(kāi)整個(gè)調(diào)用棧。相信遞歸函數(shù)的定義是正確的專注于當(dāng)前這一層邏輯“如果我已經(jīng)有了解決子問(wèn)題的函數(shù)我該如何利用它來(lái)解決當(dāng)前問(wèn)題” 比如計(jì)算階乘factorial(n) n * factorial(n-1) 你只需要相信factorial(n-1)能算出正確結(jié)果?;A(chǔ)離散數(shù)學(xué)概念集合、映射函數(shù)、布爾邏輯。這些是描述數(shù)據(jù)關(guān)系和算法邏輯的基礎(chǔ)語(yǔ)言。2.3 核心工具與思想解決問(wèn)題的套路在接觸具體數(shù)據(jù)結(jié)構(gòu)之前有一些通用的編程思想和工具能極大提升你的代碼質(zhì)量和解題能力。迭代與循環(huán)控制熟練使用for、while、do...while并能處理邊界條件例如遍歷數(shù)組時(shí)索引是從0到n-1。這是實(shí)現(xiàn)所有線性結(jié)構(gòu)操作的基礎(chǔ)。基本查找與排序雖然它們是算法但也是理解數(shù)據(jù)結(jié)構(gòu)性能的絕佳案例。順序查找O(n)最樸素的方法。二分查找O(log n)但前提是數(shù)據(jù)有序。這引出了“有序”這種數(shù)據(jù)狀態(tài)的價(jià)值。冒泡排序、選擇排序、插入排序理解它們O(n2)的由來(lái)以及它們是如何通過(guò)比較和交換來(lái)工作的。這為你后面學(xué)習(xí)更高效的排序如歸并、快排打下基礎(chǔ)。調(diào)試與測(cè)試如何設(shè)置斷點(diǎn)如何打印中間變量printf/cout如何設(shè)計(jì)簡(jiǎn)單的測(cè)試用例正常情況、邊界情況、異常情況這是你驗(yàn)證數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)是否正確、查找內(nèi)存錯(cuò)誤如訪問(wèn)越界的必備技能。2.4 抽象思維與建模能力從問(wèn)題到結(jié)構(gòu)這是最高階的預(yù)備能力也是區(qū)分普通碼農(nóng)和優(yōu)秀工程師的關(guān)鍵。它要求你能將一個(gè)具體的實(shí)際問(wèn)題抽象成適合用某種數(shù)據(jù)結(jié)構(gòu)來(lái)解決的模型。識(shí)別關(guān)鍵操作面對(duì)一個(gè)問(wèn)題首先要問(wèn)我們需要頻繁進(jìn)行哪些操作是快速查找建議哈希表、二叉搜索樹、頻繁在兩端插入刪除建議雙端隊(duì)列、維護(hù)有序性建議堆、平衡樹還是表示元素間的多對(duì)多關(guān)系建議圖權(quán)衡利弊沒(méi)有完美的數(shù)據(jù)結(jié)構(gòu)只有適合場(chǎng)景的數(shù)據(jù)結(jié)構(gòu)。數(shù)組訪問(wèn)快但增刪慢鏈表增刪快但訪問(wèn)慢。你需要學(xué)會(huì)根據(jù)“主要矛盾”做選擇。分層設(shè)計(jì)復(fù)雜系統(tǒng)往往使用多種數(shù)據(jù)結(jié)構(gòu)的組合。例如一個(gè)LRU緩存可能同時(shí)用到哈希表實(shí)現(xiàn)O(1)查找和雙向鏈表實(shí)現(xiàn)O(1)的節(jié)點(diǎn)移動(dòng)。3. 模板應(yīng)用實(shí)戰(zhàn)以“鏈表”為例的預(yù)備知識(shí)自查現(xiàn)在讓我們用這個(gè)“預(yù)備知識(shí)模板”來(lái)檢驗(yàn)一下要學(xué)好“鏈表”你需要提前打好哪些基礎(chǔ)。這就像一個(gè)行前檢查清單。假設(shè)你要實(shí)現(xiàn)一個(gè)單鏈表支持插入、刪除、遍歷操作。語(yǔ)言基礎(chǔ)自查結(jié)構(gòu)體你能正確定義一個(gè)鏈表節(jié)點(diǎn)嗎例如struct Node { int data; Node* next; };。指針你理解Node* head;這個(gè)聲明嗎head是一個(gè)指針?biāo)梢灾赶蛞粋€(gè)Node類型的對(duì)象或者為nullptr。你知道如何用-操作符通過(guò)指針訪問(wèn)成員嗎動(dòng)態(tài)內(nèi)存你知道如何用new創(chuàng)建一個(gè)新節(jié)點(diǎn)以及用delete釋放節(jié)點(diǎn)內(nèi)存嗎你能畫出head new Node();這行代碼執(zhí)行前后的內(nèi)存示意圖嗎函數(shù)參數(shù)傳遞如果你想寫一個(gè)函數(shù)insertAtHead(Node* head, int value) 為什么head參數(shù)需要是引用或二級(jí)指針Node**因?yàn)槟阋薷恼{(diào)用者外部的head指針讓它指向新的頭節(jié)點(diǎn)。如果只是Node* head 你修改的只是函數(shù)內(nèi)部這個(gè)指針變量的副本。數(shù)學(xué)與邏輯自查復(fù)雜度分析你能說(shuō)出鏈表“按索引訪問(wèn)”的時(shí)間復(fù)雜度是O(n)而“在已知節(jié)點(diǎn)后插入”的時(shí)間復(fù)雜度是O(1)嗎為什么遞歸你能用遞歸的方式遍歷鏈表并打印所有元素嗎遞歸的終止條件是什么當(dāng)前節(jié)點(diǎn)為nullptr。工具與思想自查迭代你能熟練地用while循環(huán)遍歷鏈表嗎Node* current head; while (current ! nullptr) { ... current current-next; }。邊界處理你能考慮到所有特殊情況嗎比如向空鏈表插入第一個(gè)節(jié)點(diǎn)、刪除鏈表中的唯一一個(gè)節(jié)點(diǎn)、刪除頭節(jié)點(diǎn)、處理的索引超出鏈表長(zhǎng)度等。調(diào)試當(dāng)你的鏈表程序崩潰段錯(cuò)誤時(shí)你的第一反應(yīng)是什么是檢查指針是否為nullptr就解引用了嗎是訪問(wèn)了已經(jīng)delete的內(nèi)存嗎你會(huì)用打印指針地址或調(diào)試器來(lái)跟蹤指針的指向嗎如果你對(duì)以上大部分問(wèn)題都能清晰回答那么恭喜你你的“鏈表預(yù)備知識(shí)”已經(jīng)過(guò)關(guān)可以開(kāi)始愉快地編碼實(shí)現(xiàn)了。如果有些地方模糊那就回到對(duì)應(yīng)的基石模塊去補(bǔ)強(qiáng)。這就是“預(yù)備知識(shí)模板”的用法——它不是一份待讀的清單而是一份用于自我診斷和查漏補(bǔ)缺的工具。4. 從模板到具體如何填充你的知識(shí)框架有了這個(gè)認(rèn)知框架你該如何系統(tǒng)地填充它呢我分享一個(gè)被驗(yàn)證有效的“四步學(xué)習(xí)法”。4.1 第一步針對(duì)性補(bǔ)強(qiáng)語(yǔ)言短板不要回頭去通讀一本500頁(yè)的C Primer。根據(jù)我們第二章提到的核心要點(diǎn)進(jìn)行目標(biāo)驅(qū)動(dòng)學(xué)習(xí)。行動(dòng)建議打開(kāi)你的IDE創(chuàng)建一個(gè)測(cè)試文件。針對(duì)“指針”這個(gè)主題編寫小程序來(lái)驗(yàn)證你的理解。程序1定義兩個(gè)整型變量a,b和兩個(gè)指針p1,p2讓p1指向ap2指向b。通過(guò)指針修改a,b的值并打印。程序2定義一個(gè)整型數(shù)組和一個(gè)指針用指針遍歷數(shù)組并求和。程序3寫一個(gè)函數(shù)void swap(int* a, int* b) 實(shí)現(xiàn)通過(guò)指針交換兩個(gè)變量的值。再寫一個(gè)void swap(int a, int b) 通過(guò)引用來(lái)實(shí)現(xiàn)。思考它們的異同。程序4動(dòng)態(tài)申請(qǐng)一個(gè)int數(shù)組賦值后打印最后釋放內(nèi)存。用valgrindLinux/Mac或調(diào)試器檢查是否有內(nèi)存泄漏。踩坑記錄我最開(kāi)始學(xué)指針時(shí)常犯的錯(cuò)誤是混淆“修改指針指向”和“修改指針?biāo)竷?nèi)容”。p x;是讓p指向x。*p 10;是把p當(dāng)前指向的那個(gè)變量的值改為10。這兩個(gè)操作天差地別。4.2 第二步刻意練習(xí)遞歸與復(fù)雜度分析這是兩個(gè)可以脫離具體數(shù)據(jù)結(jié)構(gòu)進(jìn)行專項(xiàng)訓(xùn)練的思維體操。遞歸練習(xí)經(jīng)典入門實(shí)現(xiàn)階乘、斐波那契數(shù)列注意遞歸效率問(wèn)題、漢諾塔。鏈表/樹模擬打印一個(gè)數(shù)字的每一位例如輸入1234輸出1 2 3 4。這本質(zhì)上是對(duì)一個(gè)“數(shù)字鏈表”的遞歸遍歷。計(jì)算一個(gè)數(shù)的各位數(shù)字之和。這些練習(xí)能幫你建立“把問(wèn)題分解為更小同類問(wèn)題”的思維。復(fù)雜度分析練習(xí)找一段簡(jiǎn)單的代碼可以是你自己寫的也可以是書上的例題遮住答案自己分析它的時(shí)間復(fù)雜度和空間復(fù)雜度。對(duì)比不同解決方案。例如判斷一個(gè)數(shù)是否為素?cái)?shù)從2遍歷到n-1是O(n)遍歷到sqrt(n)是O(√n)。這種對(duì)比能讓你直觀感受到算法優(yōu)化的威力。實(shí)操心得分析復(fù)雜度時(shí)抓住主要矛盾忽略常數(shù)項(xiàng)和低階項(xiàng)。關(guān)注循環(huán)的嵌套層數(shù)和每次循環(huán)規(guī)模如何變化。單層循環(huán)如果規(guī)模從n降到1通常是O(n)如果規(guī)模每次減半如二分查找就是O(log n)。4.3 第三步建立“數(shù)據(jù)結(jié)構(gòu)-操作-復(fù)雜度”速查表在開(kāi)始學(xué)習(xí)每個(gè)具體數(shù)據(jù)結(jié)構(gòu)時(shí)主動(dòng)為其建立一張思維卡片。以“動(dòng)態(tài)數(shù)組”如C的vector Java的ArrayList為例核心操作平均時(shí)間復(fù)雜度最壞情況時(shí)間復(fù)雜度說(shuō)明隨機(jī)訪問(wèn) (a[i])O(1)O(1)通過(guò)索引直接計(jì)算內(nèi)存地址是其最大優(yōu)勢(shì)。在尾部插入/刪除O(1)O(1)攤銷時(shí)間復(fù)雜度為O(1)。可能觸發(fā)擴(kuò)容復(fù)制但均攤到每次操作成本很低。在頭部/中部插入/刪除O(n)O(n)需要移動(dòng)后續(xù)所有元素。查找特定值O(n)O(n)需要遍歷。擴(kuò)容-O(n)申請(qǐng)新內(nèi)存并復(fù)制所有元素。把這樣的表格記在筆記里。當(dāng)你遇到一個(gè)問(wèn)題需要頻繁在中間插入時(shí)看一眼表格就知道動(dòng)態(tài)數(shù)組可能不是最佳選擇應(yīng)該考慮鏈表。這個(gè)習(xí)慣能讓你在解決問(wèn)題時(shí)快速篩選候選數(shù)據(jù)結(jié)構(gòu)。4.4 第四步從模仿實(shí)現(xiàn)到應(yīng)用解題學(xué)習(xí)分兩步走模仿實(shí)現(xiàn)找一本靠譜的教材如《數(shù)據(jù)結(jié)構(gòu)與算法分析C語(yǔ)言描述》跟著書上的代碼親手實(shí)現(xiàn)一遍基本的數(shù)據(jù)結(jié)構(gòu)鏈表、棧、隊(duì)列、二叉搜索樹。關(guān)鍵不是背代碼而是理解每一步為什么這么做。比如在鏈表插入時(shí)為什么需要先讓新節(jié)點(diǎn)指向下一個(gè)節(jié)點(diǎn)再讓前一個(gè)節(jié)點(diǎn)指向新節(jié)點(diǎn)順序反了會(huì)怎樣會(huì)丟失原鏈表的后續(xù)部分。自己畫圖把每一步指針的變化畫出來(lái)這是理解鏈表的不二法門。應(yīng)用解題在LeetCode、??途W(wǎng)等平臺(tái)上找對(duì)應(yīng)數(shù)據(jù)結(jié)構(gòu)的“標(biāo)簽題”進(jìn)行練習(xí)。鏈表練習(xí)反轉(zhuǎn)鏈表、檢測(cè)環(huán)、合并兩個(gè)有序鏈表、刪除倒數(shù)第N個(gè)節(jié)點(diǎn)。棧練習(xí)括號(hào)匹配、表達(dá)式求值、最小棧。隊(duì)列練習(xí)二叉樹的層序遍歷、滑動(dòng)窗口最大值。哈希表練習(xí)兩數(shù)之和、字母異位詞分組。樹練習(xí)三種遞歸遍歷、求深度、判斷平衡二叉樹。從“能寫出來(lái)”到“能在合適的地方用出來(lái)”這中間隔著大量的練習(xí)和總結(jié)。每做完一道題問(wèn)自己這道題的核心考點(diǎn)是什么我用的數(shù)據(jù)結(jié)構(gòu)優(yōu)勢(shì)在哪有沒(méi)有其他數(shù)據(jù)結(jié)構(gòu)可以解決時(shí)間/空間復(fù)雜度是多少5. 高級(jí)預(yù)備當(dāng)模板遇到“模板”——C泛型編程在C的語(yǔ)境下“模板”這個(gè)詞有雙重含義。除了我們討論的“學(xué)習(xí)框架模板”它還是語(yǔ)言的一個(gè)強(qiáng)大特性——泛型。當(dāng)你掌握了基本的數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)后用模板來(lái)重構(gòu)它們是邁向工業(yè)級(jí)代碼的重要一步。5.1 為什么需要泛型數(shù)據(jù)結(jié)構(gòu)你最初實(shí)現(xiàn)的鏈表可能只能存儲(chǔ)int類型。但如果明天需要存string后天需要存自定義的Student對(duì)象呢復(fù)制粘貼代碼然后修改data的類型這違反了DRYDon‘t Repeat Yourself原則維護(hù)起來(lái)是噩夢(mèng)。C的類模板允許你編寫一個(gè)“藍(lán)圖”讓編譯器為你需要的每種類型生成具體的代碼。// 一個(gè)簡(jiǎn)單的鏈表節(jié)點(diǎn)模板 template typename T // T 是一個(gè)占位符代表任意類型 struct Node { T data; // 數(shù)據(jù)域可以是任何類型 NodeT* next; // 指針域指向同類型節(jié)點(diǎn) Node(const T val) : data(val), next(nullptr) {} // 構(gòu)造函數(shù) }; // 鏈表類模板 template typename T class LinkedList { private: NodeT* head; public: LinkedList() : head(nullptr) {} void insertAtHead(const T value); // ... 其他操作 };這樣你就可以用LinkedListint來(lái)存整數(shù)用LinkedListstd::string來(lái)存字符串而底層邏輯完全一樣。5.2 學(xué)習(xí)泛型編程的預(yù)備知識(shí)要玩轉(zhuǎn)模板你需要額外準(zhǔn)備一些知識(shí)堅(jiān)實(shí)的C基礎(chǔ)包括引用、const正確性、拷貝控制拷貝構(gòu)造函數(shù)、賦值運(yùn)算符、析構(gòu)函數(shù)。因?yàn)槟0宕a中會(huì)大量涉及const T這樣的參數(shù)傳遞以及對(duì)象復(fù)制的語(yǔ)義。理解編譯器的行為模板不是真正的代碼它是一個(gè)配方。當(dāng)你寫下LinkedListint myList;時(shí)編譯器才會(huì)拿著int這個(gè)“食材”根據(jù)LinkedListT這個(gè)“配方”現(xiàn)場(chǎng)生成一份處理int的鏈表代碼。這個(gè)過(guò)程叫模板實(shí)例化。typename與class關(guān)鍵字在模板聲明中template typename T和template class T在大多數(shù)情況下可以互換。但typename有時(shí)必須用于告訴編譯器某個(gè)依賴名稱是一個(gè)類型。標(biāo)準(zhǔn)模板庫(kù)的接觸C的STL本身就是用模板構(gòu)建的龐大庫(kù)。學(xué)習(xí)使用std::vector,std::list,std::map的過(guò)程也是學(xué)習(xí)模板設(shè)計(jì)思想的過(guò)程。5.3 從具體到泛型的實(shí)踐路徑我建議按這個(gè)順序推進(jìn)先用具體類型實(shí)現(xiàn)用int完整實(shí)現(xiàn)一個(gè)數(shù)據(jù)結(jié)構(gòu)確保邏輯完全正確測(cè)試充分。將其改為模板把所有的int替換為typename T。注意函數(shù)簽名和成員變量的變化。處理邊界情況思考你的數(shù)據(jù)結(jié)構(gòu)對(duì)類型T有什么要求嗎比如你的鏈表排序函數(shù)可能需要T類型支持比較操作。這時(shí)就需要用到概念或簡(jiǎn)單的SFINAE技術(shù)對(duì)于初學(xué)者可以先假設(shè)類型支持必要操作。測(cè)試多種類型用int,double,std::string以及你自己的類來(lái)測(cè)試這個(gè)模板鏈表確保其通用性。這個(gè)過(guò)程會(huì)加深你對(duì)“抽象”和“復(fù)用”的理解這是從學(xué)生代碼走向工程代碼的關(guān)鍵一躍。你會(huì)發(fā)現(xiàn)之前為int寫的所有邏輯對(duì)于任意類型都成立這種“一招鮮吃遍天”的感覺(jué)正是編程的魅力所在。學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)就像組裝一臺(tái)精密的儀器。預(yù)備知識(shí)就是那些規(guī)格各異的螺絲刀、扳手和校準(zhǔn)工具。沒(méi)有它們你只能對(duì)著零件干瞪眼有了它們并且知道每件工具該在哪個(gè)環(huán)節(jié)使用你就能有條不紊地將其組裝成型甚至能設(shè)計(jì)出更精妙的裝置。這份“數(shù)據(jù)結(jié)構(gòu)預(yù)備知識(shí)模板”就是你的工具清單和使用指南?,F(xiàn)在對(duì)照這份清單檢查你的工具箱補(bǔ)上缺漏磨礪生銹的部分然后就可以充滿信心地開(kāi)啟你的數(shù)據(jù)結(jié)構(gòu)與算法之旅了。記住扎實(shí)的地基決定了你能建造的樓層高度。