據(jù)結(jié)構(gòu)C/C++代碼實(shí)現(xiàn)包:40+文件編譯運(yùn)行與避坑指南)
簡(jiǎn)介這份資源面向正在學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)課程、準(zhǔn)備考試或需要?jiǎng)邮謱?shí)現(xiàn)算法的同學(xué)針對(duì)課堂聽懂但代碼寫不出的常見困境提供了一套可直接參考的C/C實(shí)現(xiàn)集合。壓縮包共35個(gè)文件以34個(gè)cpp源碼為主另附1份md說明文檔整體約28KB體積輕便便于快速瀏覽與本地編譯調(diào)試。內(nèi)容覆蓋線性表、棧與隊(duì)列、串、矩陣、二叉樹與線索二叉樹、哈夫曼樹、廣義表等基礎(chǔ)結(jié)構(gòu)也包含圖的鄰接矩陣、鄰接表、十字鏈表、鄰接多重表等存儲(chǔ)方式以及BFS、DFS、Dijkstra、Floyd、Prim、Kruskal、拓?fù)渑判蚝完P(guān)鍵路徑等經(jīng)典算法基本對(duì)應(yīng)數(shù)據(jù)結(jié)構(gòu)課程的核心章節(jié)。目前已有369人學(xué)習(xí)適合作為課程實(shí)驗(yàn)、期末復(fù)習(xí)與算法入門的對(duì)照材料讀者可借此理解結(jié)構(gòu)定義、算法流程與代碼組織方式并在此基礎(chǔ)上自行修改與擴(kuò)展。1. 從一份 40 多個(gè) cpp 文件的數(shù)據(jù)結(jié)構(gòu)代碼包說起如果你正在準(zhǔn)備數(shù)據(jù)結(jié)構(gòu)期末、考研 408或者帶大一實(shí)驗(yàn)課大概率會(huì)遇到同一個(gè)尷尬課本上的偽代碼看懂了真讓你從零寫一個(gè)帶模板、能編譯、邊界不崩的 C/C 實(shí)現(xiàn)手還是抖的。這份「數(shù)據(jù)結(jié)構(gòu)C-C代碼實(shí)現(xiàn).rar」就是沖著這個(gè)痛點(diǎn)來的——它不是一份 PDF 講義而是一整包可以直接丟進(jìn) Dev-C 或 VS Code 里編譯運(yùn)行的.cpp源文件覆蓋線性表、棧隊(duì)列、串、樹、圖、排序查找這幾大塊。適合誰適合已經(jīng)聽過課、但缺一份「能跑起來對(duì)照」的參考實(shí)現(xiàn)的人也適合當(dāng)實(shí)驗(yàn)報(bào)告的骨架自己改注釋、改輸入輸出。不適合完全零基礎(chǔ)、連指針和結(jié)構(gòu)體都沒寫過的人那樣你只會(huì)復(fù)制粘貼編譯報(bào)錯(cuò)都看不懂。下面我按「包里有什么 → 怎么編譯跑通 → 各模塊怎么用 → 坑在哪 → 怎么進(jìn)階」的順序拆一遍。2. 拆包看結(jié)構(gòu)40 多個(gè)文件到底覆蓋了哪些數(shù)據(jù)結(jié)構(gòu)2.1 文件清單與知識(shí)點(diǎn)映射先把包里的文件按知識(shí)模塊歸一下類這樣你打開文件夾不會(huì)懵。文件名基本就是內(nèi)容命名風(fēng)格偏學(xué)生作業(yè)有幾個(gè)像gfdg.cpp、hfdhj.cpp這種隨手起的名字需要打開看才知道是什么。模塊對(duì)應(yīng)文件說明線性表順序表.cpp、單鏈表.cpp、雙向鏈表.cpp、鏈棧.cpp、鏈隊(duì).cpp、隊(duì)列.cpp、棧.cpp順序存儲(chǔ)與鏈?zhǔn)酱鎯?chǔ)對(duì)照串與廣義表串.cpp、廣義表.cpp、矩陣.cpp字符串操作、廣義表遞歸、稀疏矩陣樹二叉樹.cpp、線索二叉樹.cpp、哈夫曼樹.cpp、哈夫曼樹編碼.cpp遍歷、線索化、編碼圖鄰接矩陣創(chuàng)建圖.cpp、鄰接表創(chuàng)建圖.cpp、十字鏈表.cpp、鄰接多重表.cpp四種存儲(chǔ)結(jié)構(gòu)圖算法DFS.cpp、BFS.cpp、Prim.cpp、Kruskal.cpp、Dijkstra.cpp、Floyd.cpp、拓?fù)渑判?cpp、關(guān)鍵路徑.cpp最小生成樹、最短路、AOV/AOE遞歸與習(xí)題Hanoi.cpp、括號(hào)的匹配.cpp、表達(dá)式求值.cpp、數(shù)制的轉(zhuǎn)換.cpp、表合并.cpp、舞伴問題.cpp棧和遞歸的經(jīng)典應(yīng)用文檔數(shù)據(jù)結(jié)構(gòu).md說明或筆記這張表的價(jià)值在于你復(fù)習(xí)到哪個(gè)知識(shí)點(diǎn)直接定位到對(duì)應(yīng)文件不用一個(gè)個(gè)點(diǎn)開猜。比如復(fù)習(xí)到最小生成樹Prim.cpp和Kruskal.cpp放一起對(duì)比一個(gè)稠密圖一個(gè)稀疏圖選型理由一目了然。2.2 命名混亂的文件怎么處理gfdg.cpp、hfdhj.cpp這兩個(gè)名字沒有任何信息量常見做法是先看文件大小和開頭幾行注釋。如果開頭有// 哈夫曼之類的字樣就直接歸類如果沒有看它#include了什么、定義了哪些結(jié)構(gòu)體。我一般會(huì)先編譯一遍能跑通再看輸出判斷功能。這類文件大概率是作者當(dāng)時(shí)練手留下的半成品別指望它有多完整遇到編譯不過的直接跳過不影響主線學(xué)習(xí)。提示解壓后先別急著全選編譯包里文件互相獨(dú)立每個(gè).cpp基本都有自己的main函數(shù)一起編譯必然重定義沖突。3. 把代碼跑起來編譯環(huán)境、單文件編譯與常見報(bào)錯(cuò)3.1 環(huán)境選擇Dev-C 還是 VS Code這份代碼是典型的教學(xué)風(fēng)格 C/C大量使用struct、指針、malloc/new混用還有iostream.h時(shí)代遺留的寫法可能出現(xiàn)在個(gè)別文件里。最省事的是 Dev-C開箱即用新建項(xiàng)目把單個(gè) cpp 拖進(jìn)去就能編譯。如果你習(xí)慣 VS Code需要自己配 MinGW參考「vscode配置c/c環(huán)境」那套流程裝 MinGW-w64、配tasks.json和launch.json。新手我建議先用 Dev-C 把代碼跑通確認(rèn)邏輯沒問題再遷到 VS Code 練工程化。3.2 單文件編譯命令因?yàn)槊總€(gè)文件獨(dú)立最穩(wěn)的方式是單獨(dú)編譯。命令行下# 進(jìn)入解壓目錄逐個(gè)編譯-o 指定輸出名避免覆蓋 g 順序表.cpp -o seqlist g 單鏈表.cpp -o linklist g Dijkstra.cpp -o dijkstra # 運(yùn)行 ./seqlistWindows 下把./換成直接敲seqlist.exe。如果你用 Dev-C直接文件 - 打開選中某個(gè) cpp按 F11 編譯運(yùn)行即可。參數(shù)說明-o后面跟輸出文件名不加的話默認(rèn)生成a.exeWindows或a.outLinux多個(gè)文件連著編譯會(huì)互相覆蓋所以務(wù)必每個(gè)都指定不同名字。-g可以加上用于調(diào)試-Wall打開警告能提前發(fā)現(xiàn)未初始化變量這類問題。3.3 編譯報(bào)錯(cuò)的典型處理教學(xué)代碼最常見的三類報(bào)錯(cuò)一是iostream.h找不到改成#include iostream并加using namespace std;二是malloc返回值沒強(qiáng)轉(zhuǎn)C 里會(huì)報(bào)invalid conversion from void* to ...加(類型*)強(qiáng)轉(zhuǎn)三是main函數(shù)寫了void main()標(biāo)準(zhǔn)要求int main()并return 0。遇到報(bào)錯(cuò)先看行號(hào)八成是這三類。改完再編譯別一次改十個(gè)地方否則新錯(cuò)誤蓋舊錯(cuò)誤排查起來就是黑匣子。4. 核心模塊怎么用從線性表到圖算法的實(shí)操要點(diǎn)4.1 線性表與棧隊(duì)列先跑通再改順序表.cpp和單鏈表.cpp是整包的地基建議第一個(gè)跑。順序表重點(diǎn)看插入刪除時(shí)的元素搬移注意下標(biāo)從 0 還是 1 開始——教學(xué)代碼兩種都有跑之前先看main里的調(diào)用。單鏈表重點(diǎn)看頭插和尾插的區(qū)別以及刪除節(jié)點(diǎn)時(shí)free/delete的時(shí)機(jī)漏了就是內(nèi)存泄漏。// 單鏈表插入的典型寫法注意指針順序不能反 Node* newNode new Node; newNode-data value; newNode-next p-next; // 先接后面 p-next newNode; // 再斷前面邏輯說明這兩行的順序如果寫反先p-next newNode再newNode-next p-next此時(shí)p-next已經(jīng)指向新節(jié)點(diǎn)等于自己指自己鏈表斷裂。這是鏈?zhǔn)浇Y(jié)構(gòu)最高頻的翻車點(diǎn)沒有之一。棧和隊(duì)列對(duì)照看棧.cpp大概率是順序棧鏈棧.cpp是鏈?zhǔn)疥?duì)列.cpp注意循環(huán)隊(duì)列的front/rear和判滿條件(rear1)%maxsizefront這個(gè)取模判滿是最容易記混的。鏈隊(duì).cpp看隊(duì)頭隊(duì)尾指針的維護(hù)。4.2 樹與哈夫曼遞歸是主線二叉樹.cpp里前中后序和層序遍歷都有遞歸寫法是主線。重點(diǎn)理解遞歸函數(shù)的參數(shù)和返回時(shí)機(jī)。線索二叉樹.cpp是難點(diǎn)核心是ltag/rtag標(biāo)志位和遍歷時(shí)找前驅(qū)后繼的規(guī)則建議先畫一棵三節(jié)點(diǎn)的樹手動(dòng)走一遍再讀代碼。哈夫曼樹.cpp和哈夫曼樹編碼.cpp配套看前者建樹后者生成 01 編碼注意優(yōu)先隊(duì)列或每次找兩個(gè)最小權(quán)值的實(shí)現(xiàn)方式。// 哈夫曼建樹核心每次取兩個(gè)最小權(quán)值合并 while (隊(duì)列中節(jié)點(diǎn)數(shù) 1) { a 取最小; b 取次小; newNode new Node(a-weight b-weight); newNode-left a; newNode-right b; 把 newNode 放回隊(duì)列; }參數(shù)說明weight是權(quán)值合并后的新節(jié)點(diǎn)權(quán)值是兩者之和放回隊(duì)列參與下一輪。循環(huán)結(jié)束隊(duì)列里剩的那個(gè)就是根節(jié)點(diǎn)。這里如果用數(shù)組實(shí)現(xiàn)「取最小」記得每次取完要標(biāo)記已用否則會(huì)重復(fù)取同一個(gè)節(jié)點(diǎn)。4.3 圖算法四種存儲(chǔ)加八個(gè)算法圖這塊是整包最厚的部分。存儲(chǔ)結(jié)構(gòu)四個(gè)文件對(duì)照看鄰接矩陣適合稠密圖鄰接表適合稀疏圖十字鏈表針對(duì)有向圖鄰接多重表針對(duì)無向圖。算法部分DFS.cpp/BFS.cpp遍歷基礎(chǔ)注意 visited 數(shù)組的初始化位置放在函數(shù)外全局還是每次調(diào)用前重置直接影響多次遍歷結(jié)果。Prim.cpp/Kruskal.cpp最小生成樹。Prim 從一個(gè)點(diǎn)擴(kuò)展適合稠密圖Kruskal 按邊排序加并查集適合稀疏圖。Dijkstra.cpp/Floyd.cpp最短路。Dijkstra 單源非負(fù)權(quán)Floyd 多源三重循環(huán)順序不能錯(cuò)。拓?fù)渑判?cpp/關(guān)鍵路徑.cppAOV 網(wǎng)和 AOE 網(wǎng)拓?fù)渑判蛴萌攵葹榱闳腙?duì)關(guān)鍵路徑在拓?fù)湫蚧A(chǔ)上算最早最晚時(shí)間。// Floyd 三重循環(huán)k 必須在最外層 for (int k 0; k n; k) for (int i 0; i n; i) for (int j 0; j n; j) if (dist[i][k] dist[k][j] dist[i][j]) dist[i][j] dist[i][k] dist[k][j];邏輯說明k放最外層是 Floyd 正確性的關(guān)鍵它代表「允許經(jīng)過的中轉(zhuǎn)點(diǎn)集合逐步擴(kuò)大」。如果寫成i最外層結(jié)果會(huì)錯(cuò)這是考試和面試都愛考的細(xì)節(jié)。dist初始化為鄰接矩陣自己到自己為 0無邊為無窮大。5. 避坑與排查這份代碼包里最容易翻車的五件事5.1 多個(gè) main 函數(shù)導(dǎo)致鏈接沖突現(xiàn)象把幾個(gè) cpp 一起加入項(xiàng)目編譯報(bào)multiple definition of main。原因每個(gè)文件都有獨(dú)立main是設(shè)計(jì)給單獨(dú)運(yùn)行的。解決一次只編譯一個(gè)文件或者把要保留的main留下其余改成普通函數(shù)并注釋掉各自的main。5.2 輸入格式和代碼預(yù)期不一致現(xiàn)象程序跑起來卡住或輸出亂碼。原因教學(xué)代碼的cin/scanf對(duì)輸入格式有隱含要求比如先輸節(jié)點(diǎn)數(shù)再輸邊你少輸一個(gè)數(shù)它就錯(cuò)位。解決打開main看輸入順序按注釋里的格式喂數(shù)據(jù)別憑感覺輸。5.3 數(shù)組越界與未初始化現(xiàn)象結(jié)果偶爾對(duì)偶爾錯(cuò)或者直接崩潰。原因教學(xué)代碼常用固定大小數(shù)組如int a[100]節(jié)點(diǎn)數(shù)超了就溢出visited 數(shù)組沒清零導(dǎo)致上次遍歷的殘留影響本次。解決把數(shù)組開大或在每次算法調(diào)用前memset(visited, 0, sizeof(visited))。5.4 內(nèi)存泄漏與野指針現(xiàn)象程序能跑但長(zhǎng)時(shí)間運(yùn)行內(nèi)存漲或刪除節(jié)點(diǎn)后訪問崩潰。原因new/malloc后沒配對(duì)delete/free或刪除后指針沒置空。解決刪除節(jié)點(diǎn)后立刻p nullptr養(yǎng)成習(xí)慣。教學(xué)代碼這塊普遍不嚴(yán)謹(jǐn)自己補(bǔ)上。5.5 中文注釋導(dǎo)致的編碼報(bào)錯(cuò)現(xiàn)象Dev-C 里中文注釋變亂碼甚至編譯報(bào)錯(cuò)。原因文件編碼是 GBK而編輯器按 UTF-8 解析。解決在 Dev-C 里設(shè)置「工具 - 編輯器選項(xiàng) - 編碼」為 GBK或把文件轉(zhuǎn)成 UTF-8。VS Code 右下角點(diǎn)編碼切換即可。6. 進(jìn)階用法把散裝代碼改成可復(fù)用模板與驗(yàn)證方法跑通單個(gè)文件只是第一步真正讓這份包產(chǎn)生長(zhǎng)期價(jià)值的是把它改造成自己的模板庫(kù)。我的做法是先挑出順序表.cpp、單鏈表.cpp、二叉樹.cpp、Dijkstra.cpp這四個(gè)高頻文件把里面的struct和核心函數(shù)抽出來去掉main改成.h頭文件加.cpp實(shí)現(xiàn)用#ifndef做防重復(fù)包含。這樣以后寫實(shí)驗(yàn)或刷題直接#include seqlist.h就能用不用每次重抄。// seqlist.h 抽取示例 #ifndef SEQLIST_H #define SEQLIST_H #define MAXSIZE 100 typedef struct { int data[MAXSIZE]; int length; } SeqList; void InitList(SeqList L); bool ListInsert(SeqList L, int i, int e); bool ListDelete(SeqList L, int i, int e); #endif邏輯說明#ifndef防止頭文件被多次包含導(dǎo)致重定義把MAXSIZE提出來方便統(tǒng)一改容量函數(shù)聲明和實(shí)現(xiàn)分離實(shí)現(xiàn)放.cpp里編譯成目標(biāo)文件。這樣一套下來你就有了自己的小型數(shù)據(jù)結(jié)構(gòu)庫(kù)。驗(yàn)證方法上別只看「能跑」要構(gòu)造邊界用例。順序表測(cè)空表刪除、滿表插入鏈表測(cè)刪除頭節(jié)點(diǎn)、刪除尾節(jié)點(diǎn)Dijkstra 測(cè)有不可達(dá)節(jié)點(diǎn)的圖看輸出是不是無窮大Floyd 測(cè)負(fù)權(quán)邊雖然 Dijkstra 不支持但 Floyd 可以驗(yàn)證三重循環(huán)順序。每改一處就回歸測(cè)一遍比事后 debug 省事得多。還有個(gè)實(shí)用技巧用隨機(jī)數(shù)生成測(cè)試數(shù)據(jù)。C 里rand()配合srand(time(0))生成隨機(jī)圖或隨機(jī)序列喂給算法跑再和暴力解法對(duì)拍。比如最小生成樹隨機(jī)生成 10 個(gè)點(diǎn)的圖Prim 和 Kruskal 各跑一遍結(jié)果權(quán)值必須相等不等就是有 bug。這種對(duì)拍方法比手寫用例高效得多也是我后來做工程養(yǎng)成的習(xí)慣。注意對(duì)拍時(shí)兩個(gè)算法的輸入必須完全一致建議先把圖存到文件里兩個(gè)程序讀同一個(gè)文件避免生成隨機(jī)數(shù)時(shí)種子不同導(dǎo)致輸入不同。從那以后我每次拿到一份別人寫的教學(xué)代碼都強(qiáng)制先跑通一個(gè)最小用例再上邊界和對(duì)拍絕不直接信「能編譯就是對(duì)的」。這份包的價(jià)值不在于代碼寫得多優(yōu)雅而在于它把數(shù)據(jù)結(jié)構(gòu)從偽代碼落成了能編譯、能調(diào)試、能改的實(shí)物你照著改一遍比看十遍書都管用。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取