高级线性表——静态链表(最全静态链表解读)
寫在前面:博主是一位普普通通的19屆雙非軟工在讀生,平時(shí)最大的愛好就是聽聽歌,逛逛B站。博主很喜歡的一句話花開堪折直須折,莫待無花空折枝:博主的理解是頭一次為人,就應(yīng)該做自己想做的事,做自己不后悔的事,做自己以后不會留有遺憾的事,做自己覺得有意義的事,不浪費(fèi)這大好的青春年華。博主寫博客目的是記錄所學(xué)到的知識并方便自己復(fù)習(xí),在記錄知識的同時(shí)獲得部分瀏覽量,得到更多人的認(rèn)可,滿足小小的成就感,同時(shí)在寫博客的途中結(jié)交更多志同道合的朋友,讓自己在技術(shù)的路上并不孤單。
目錄:
1.靜態(tài)鏈表的概述
2.靜態(tài)鏈表的實(shí)現(xiàn)
???? ?? 靜態(tài)鏈表的結(jié)點(diǎn)
???? ?? 備用鏈表
???? ?? 靜態(tài)鏈表的詳細(xì)實(shí)現(xiàn)過程
3.靜態(tài)鏈表的代碼實(shí)現(xiàn)
???? ?? 靜態(tài)鏈表初始化
???? ?? 靜態(tài)鏈表的提取分配空間
???? ?? 靜態(tài)鏈表添加元素
???? ?? 釋放空間
???? ?? 靜態(tài)鏈表刪除元素
???? ?? 修改元素
???? ?? 查找元素
4.靜態(tài)鏈表的完整代碼實(shí)現(xiàn)及演示
5.靜態(tài)鏈表和動態(tài)鏈表的聯(lián)系和區(qū)別
???? ?? 靜態(tài)鏈表
???? ?? 動態(tài)鏈表
1.靜態(tài)鏈表的概述
我們了解線性表兩種存儲結(jié)構(gòu)各自的特點(diǎn),那么,是否存在一種存儲結(jié)構(gòu),可以融合順序表和鏈表各自的優(yōu)點(diǎn),從而 既能快速訪問元素,又能快速增加或刪除數(shù)據(jù)元素。
使用靜態(tài)鏈表存儲數(shù)據(jù),數(shù)據(jù)全部存儲在數(shù)組中(和順序表一樣),但存儲位置是隨機(jī)的,數(shù)據(jù)之間"一對一"的邏輯關(guān)系通過一個(gè)整形變量(稱為"游標(biāo)",和指針功能類似)維持(和鏈表類似)。
數(shù)據(jù)存放到數(shù)組中時(shí),給各個(gè)數(shù)據(jù)元素配備一個(gè)整形變量,此變量用于指明各個(gè)元素的直接后繼元素所在數(shù)組中的位置下標(biāo),如圖 :
通常,靜態(tài)鏈表會將第一個(gè)數(shù)據(jù)元素放到數(shù)組下標(biāo)為 1 的位置(a[1])中。
上圖中,從 a[1] 存儲的數(shù)據(jù)元素 1 開始,通過存儲的游標(biāo)變量 3,就可以在 a[3] 中找到元素 1 的直接后繼元素 2;同樣,通過元素 a[3] 存儲的游標(biāo)變量 5,可以在 a[5] 中找到元素 2 的直接后繼元素 3,這樣的循環(huán)過程直到某元素的游標(biāo)變量為 0 截止(因?yàn)?a[0] 默認(rèn)不存儲數(shù)據(jù)元素)。
如上通過 "數(shù)組+游標(biāo)" 的方式存儲具有線性關(guān)系數(shù)據(jù)的存儲結(jié)構(gòu)就是靜態(tài)鏈表。
2.靜態(tài)鏈表的實(shí)現(xiàn)
2.1靜態(tài)鏈表的結(jié)點(diǎn)
靜態(tài)鏈表存儲數(shù)據(jù)元素也需要自定義數(shù)據(jù)類型,至少需要包含以下 2 部分信息:
2.2備用鏈表
其實(shí)上面顯示的靜態(tài)鏈表還不夠完整,靜態(tài)鏈表中,除了數(shù)據(jù)本身通過游標(biāo)組成的鏈表外,還需要有一條連接各個(gè)空閑位置的鏈表,稱為備用鏈表。
備用鏈表的作用是:
回收數(shù)組中未使用或之前使用過(目前未使用)的存儲空間,留待后期使用。
也就是說,靜態(tài)鏈表使用數(shù)組申請的物理空間中,存有兩個(gè)鏈表,一條連接數(shù)據(jù),另一條連接數(shù)組中未使用的空間。通常,備用鏈表的表頭位于數(shù)組下標(biāo)為 0(a[0]) 的位置,而數(shù)據(jù)鏈表的表頭位于數(shù)組下標(biāo)為 1(a[1])的位置。
靜態(tài)鏈表中設(shè)置備用鏈表的好處是:
可以清楚地知道數(shù)組中是否有空閑位置,以便數(shù)據(jù)鏈表添加新數(shù)據(jù)時(shí)使用。比如,若靜態(tài)鏈表中數(shù)組下標(biāo)為 0 的位置上存有數(shù)據(jù),則證明數(shù)組已滿。
例如,使用靜態(tài)鏈表存儲 {1,2,3},假設(shè)使用長度為 6 的數(shù)組 a,則存儲狀態(tài)可能如圖所示:
上圖備用鏈表上連接的依次是 a[0]、a[2] 和 a[4],而數(shù)據(jù)鏈表上連接的依次是 a[1]、a[3] 和 a[5]。
2.3靜態(tài)鏈表的詳細(xì)實(shí)現(xiàn)過程
假設(shè)使用靜態(tài)鏈表(數(shù)組長度為 6)存儲 {1,2,3},則需經(jīng)歷以下幾個(gè)階段。
1.在數(shù)據(jù)鏈表未初始化之前,數(shù)組中所有位置都處于空閑狀態(tài),因此都應(yīng)被鏈接在備用鏈表上
當(dāng)向靜態(tài)鏈表中添加數(shù)據(jù)時(shí),需提前從備用鏈表中摘除節(jié)點(diǎn),以供新數(shù)據(jù)使用。
備用鏈表摘除節(jié)點(diǎn)最簡單的方法是摘除 a[0] 的直接后繼節(jié)點(diǎn);同樣,向備用鏈表中添加空閑節(jié)點(diǎn)也是添加作為 a[0] 新的直接后繼節(jié)點(diǎn)。因?yàn)?a[0] 是備用鏈表的第一個(gè)節(jié)點(diǎn),我們知道它的位置,操作它的直接后繼節(jié)點(diǎn)相對容易,無需遍歷備用鏈表,耗費(fèi)的時(shí)間復(fù)雜度為 O(1)。
2 .因此,在上圖的基礎(chǔ)上,向靜態(tài)鏈表中添加元素 1 的過程下圖 所示:
添加元素 2 的過程如圖:
添加元素3的過程如圖
當(dāng)然我們有時(shí)候會看到靜態(tài)鏈表最后數(shù)組最后一個(gè)位置的數(shù)據(jù)域不存放任何東西,游標(biāo)域存放首元結(jié)點(diǎn)的數(shù)組下標(biāo)(相當(dāng)于頭結(jié)點(diǎn))
3.靜態(tài)鏈表的代碼實(shí)現(xiàn)(最后一個(gè)數(shù)組元素相當(dāng)于頭結(jié)點(diǎn))
3.1靜態(tài)鏈表初始化
void InitArr(component *array) {int i = 0;for (i = 0; i < maxSize; i++) {array[i].cur = i + 1;//將每個(gè)數(shù)組分量鏈接到一起array[i].data = 0;}array[maxSize - 1].cur = 0;//鏈表最后一個(gè)結(jié)點(diǎn)的游標(biāo)值為0 }3.2靜態(tài)鏈表的提取分配空間
int mallocArr(component * array) {//若備用鏈表非空,則返回分配的結(jié)點(diǎn)下標(biāo),否則返回 0(當(dāng)分配最后一個(gè)結(jié)點(diǎn)時(shí),//該結(jié)點(diǎn)的游標(biāo)值為 0)int i = array[0].cur;if (array[0].cur) {array[0].cur = array[i].cur;}return i; }3.3靜態(tài)鏈表添加元素
//向鏈表中插入數(shù)據(jù),body表示鏈表的頭結(jié)點(diǎn)在數(shù)組中的位置,add表示插入元素的位置,num表示要插入的數(shù)據(jù) void insertArr(component * array, int body, int add, int num) {int tempBody = body;//tempBody做遍歷結(jié)構(gòu)體數(shù)組使用int i = 0, insert = 0;//找到要插入位置的上一個(gè)結(jié)點(diǎn)在數(shù)組中的位置for (i = 1; i < add; i++) {tempBody = array[tempBody].cur;}insert = mallocArr(array);//申請空間,準(zhǔn)備插入array[insert].data = num;array[insert].cur = array[tempBody].cur;//新插入結(jié)點(diǎn)的游標(biāo)等于其直接前驅(qū)結(jié)點(diǎn)的游標(biāo)array[tempBody].cur = insert;//直接前驅(qū)結(jié)點(diǎn)的游標(biāo)等于新插入結(jié)點(diǎn)所在數(shù)組中的下標(biāo) }3.4釋放空間
void freeArr(component * array, int k) {array[k].cur = array[0].cur;array[0].cur = k; }3.5靜態(tài)鏈表刪除元素
//刪除結(jié)點(diǎn)函數(shù),num表示被刪除結(jié)點(diǎn)中數(shù)據(jù)域存放的數(shù)據(jù) int deletArr(component * array, int num) {int tempBody =maxSize-1;int del = 0;int newbody = 0;//找到被刪除結(jié)點(diǎn)的位置while (array[tempBody].data != num) {tempBody = array[tempBody].cur;//當(dāng)tempBody為0時(shí),表示鏈表遍歷結(jié)束,說明鏈表中沒有存儲該數(shù)據(jù)的結(jié)點(diǎn)if (tempBody == 0) {printf("鏈表中沒有此數(shù)據(jù)");return 0;}}//運(yùn)行到此,證明有該結(jié)點(diǎn)del = tempBody;tempBody = maxSize-1;//刪除首元結(jié)點(diǎn),需要特殊考慮if (del == array[maxSize-1].cur) {newbody = array[del].cur;array[maxSize-1].cur=newbody; freeArr(array, del);}else{//找到該結(jié)點(diǎn)的上一個(gè)結(jié)點(diǎn),做刪除操作while (array[tempBody].cur != del) {tempBody = array[tempBody].cur;}//將被刪除結(jié)點(diǎn)的游標(biāo)直接給被刪除結(jié)點(diǎn)的上一個(gè)結(jié)點(diǎn)array[tempBody].cur = array[del].cur;//回收被摘除節(jié)點(diǎn)的空間freeArr(array, del);} }3.6修改元素
void amendElem(component * array, int oldElem, int newElem) {int add = selectNum(array,oldElem);if (add == -1) {printf("無更改元素");return;}array[add].data = newElem; }3.7查找元素
int selectNum(component * array, int num) {//當(dāng)游標(biāo)值為0時(shí),表示鏈表結(jié)束int tempBody = maxSize-1; while (array[tempBody].cur != 0) {if (array[tempBody].data == num) {return tempBody;}tempBody = array[tempBody].cur;}//判斷最后一個(gè)結(jié)點(diǎn)是否符合要求if (array[tempBody].data == num) {return tempBody;}return -1;//返回-1,表示在鏈表中沒有找到該元素 }4.靜態(tài)鏈表完整代碼實(shí)現(xiàn)及演示
#include <stdio.h> #define maxSize 6 typedef struct {int data;int cur; }component; void InitArr(component *array) {int i = 0;for (i = 0; i < maxSize; i++) {array[i].cur = i + 1;//將每個(gè)數(shù)組分量鏈接到一起array[i].data = 0;}array[maxSize - 1].cur = 0;//鏈表最后一個(gè)結(jié)點(diǎn)的游標(biāo)值為0 } int mallocArr(component * array) {//若備用鏈表非空,則返回分配的結(jié)點(diǎn)下標(biāo),否則返回 0(當(dāng)分配最后一個(gè)結(jié)點(diǎn)時(shí),//該結(jié)點(diǎn)的游標(biāo)值為 0)int i = array[0].cur;if (array[0].cur) {array[0].cur = array[i].cur;}return i; } //初始化幾個(gè)元素 void add(component * array) {printf("請輸入你想添加的數(shù)據(jù)成員的數(shù)量:");int n;scanf("%d",&n);for(int i=1;i<=n;i++){printf("請輸入第%d個(gè)數(shù)據(jù)成員:",i);scanf("%d",&array[i].data); }array[0].cur=n+1;array[n].cur=0;array[maxSize-1].cur=1; } //向鏈表中插入數(shù)據(jù),add表示插入元素的位置,num表示要插入的數(shù)據(jù) void insertArr(component * array, int add, int num) {int tempBody = maxSize-1;//tempBody做遍歷結(jié)構(gòu)體數(shù)組使用int i = 0, insert = 0;//找到要插入位置的上一個(gè)結(jié)點(diǎn)在數(shù)組中的位置for (i = 1; i < add; i++) {tempBody = array[tempBody].cur;}insert = mallocArr(array);//申請空間,準(zhǔn)備插入array[insert].data = num;array[insert].cur = array[tempBody].cur;//新插入結(jié)點(diǎn)的游標(biāo)等于其直接前驅(qū)結(jié)點(diǎn)的游標(biāo)array[tempBody].cur = insert;//直接前驅(qū)結(jié)點(diǎn)的游標(biāo)等于新插入結(jié)點(diǎn)所在數(shù)組中的下標(biāo) } void displayArr(component * array) {int tempBody = maxSize-1;//tempBody準(zhǔn)備做遍歷使用tempBody=array[tempBody].cur;while (array[tempBody].cur) {printf("%d,%d ", array[tempBody].data, array[tempBody].cur);tempBody = array[tempBody].cur;}printf("%d,%d\n", array[tempBody].data, array[tempBody].cur); } //查找數(shù)據(jù)域?yàn)閑lem的結(jié)點(diǎn)在數(shù)組中的位置 int selectNum(component * array, int num) {//當(dāng)游標(biāo)值為0時(shí),表示鏈表結(jié)束int tempBody = maxSize-1; while (array[tempBody].cur != 0) {if (array[tempBody].data == num) {return tempBody;}tempBody = array[tempBody].cur;}//判斷最后一個(gè)結(jié)點(diǎn)是否符合要求if (array[tempBody].data == num) {return tempBody;}return -1;//返回-1,表示在鏈表中沒有找到該元素 }//在以body作為頭結(jié)點(diǎn)的鏈表中將數(shù)據(jù)域?yàn)閛ldElem的結(jié)點(diǎn),數(shù)據(jù)域改為newElem void amendElem(component * array, int oldElem, int newElem) {int add = selectNum(array,oldElem);if (add == -1) {printf("無更改元素");return;}array[add].data = newElem; }void freeArr(component * array, int k) {array[k].cur = array[0].cur;array[0].cur = k; }//刪除結(jié)點(diǎn)函數(shù),num表示被刪除結(jié)點(diǎn)中數(shù)據(jù)域存放的數(shù)據(jù),函數(shù)返回新數(shù)據(jù)鏈表的表頭位置 int deletArr(component * array, int num) {int tempBody =maxSize-1;int del = 0;int newbody = 0;//找到被刪除結(jié)點(diǎn)的位置while (array[tempBody].data != num) {tempBody = array[tempBody].cur;//當(dāng)tempBody為0時(shí),表示鏈表遍歷結(jié)束,說明鏈表中沒有存儲該數(shù)據(jù)的結(jié)點(diǎn)if (tempBody == 0) {printf("鏈表中沒有此數(shù)據(jù)");return 0;}}//運(yùn)行到此,證明有該結(jié)點(diǎn)del = tempBody;tempBody = maxSize-1;//刪除首元結(jié)點(diǎn),需要特殊考慮if (del == array[maxSize-1].cur) {newbody = array[del].cur;array[maxSize-1].cur=newbody; freeArr(array, del);}else{//找到該結(jié)點(diǎn)的上一個(gè)結(jié)點(diǎn),做刪除操作while (array[tempBody].cur != del) {tempBody = array[tempBody].cur;}//將被刪除結(jié)點(diǎn)的游標(biāo)直接給被刪除結(jié)點(diǎn)的上一個(gè)結(jié)點(diǎn)array[tempBody].cur = array[del].cur;//回收被摘除節(jié)點(diǎn)的空間freeArr(array, del);} } int main() {component array[maxSize];InitArr(array);add(array);int selectAdd;printf("靜態(tài)鏈表為:\n");displayArr(array);printf("在第3的位置上插入元素4:\n");insertArr(array, 3, 4);displayArr(array);printf("刪除數(shù)據(jù)域?yàn)?的結(jié)點(diǎn):\n");deletArr(array, 1);displayArr(array);printf("查找數(shù)據(jù)域?yàn)?的結(jié)點(diǎn)的位置:\n");selectAdd = selectNum(array, 4);printf("%d\n", selectAdd);printf("將結(jié)點(diǎn)數(shù)據(jù)域?yàn)?改為5:\n");amendElem(array, 4, 5);displayArr(array);return 0;}靜態(tài)鏈表和動態(tài)鏈表的聯(lián)系和區(qū)別
5.1靜態(tài)鏈表
使用靜態(tài)鏈表存儲數(shù)據(jù),需要預(yù)先申請足夠大的一整塊內(nèi)存空間,也就是說,靜態(tài)鏈表存儲數(shù)據(jù)元素的個(gè)數(shù)從其創(chuàng)建的那一刻就已經(jīng)確定,后期無法更改。
比如,如果創(chuàng)建靜態(tài)鏈表時(shí)只申請存儲 10 個(gè)數(shù)據(jù)元素的空間,那么在使用靜態(tài)鏈表時(shí),數(shù)據(jù)的存儲個(gè)數(shù)就不能超過 10 個(gè),否則程序就會發(fā)生錯(cuò)誤。不僅如此,靜態(tài)鏈表是在固定大小的存儲空間內(nèi)隨機(jī)存儲各個(gè)數(shù)據(jù)元素,這就造成了靜態(tài)鏈表中需要使用另一條鏈表(通常稱為"備用鏈表")來記錄空間存儲空間的位置,以便后期分配給新添加元素使用,如圖 2 所示。這意味著,如果你選擇使用靜態(tài)鏈表存儲數(shù)據(jù),你需要通過操控兩條鏈表,一條是存儲數(shù)據(jù),另一條是記錄空閑空間的位置。
有一點(diǎn)需要特別的注意:
有的靜態(tài)鏈表最后一個(gè)數(shù)組下標(biāo)存放的是頭結(jié)點(diǎn),而有的靜態(tài)鏈表最后一個(gè)數(shù)組下標(biāo)和普通數(shù)組下標(biāo)作用一樣
5.2動態(tài)鏈表(普通鏈表)
使用動態(tài)鏈表存儲數(shù)據(jù),不需要預(yù)先申請內(nèi)存空間,而是在需要的時(shí)候才向內(nèi)存申請。也就是說,動態(tài)鏈表存儲數(shù)據(jù)元素的個(gè)數(shù)是不限的,想存多少就存多少。
同時(shí),使用動態(tài)鏈表的整個(gè)過程,你也只需操控一條存儲數(shù)據(jù)的鏈表。當(dāng)表中添加或刪除數(shù)據(jù)元素時(shí),你只需要通過 malloc 或free 函數(shù)來申請或釋放空間即可,實(shí)現(xiàn)起來比較簡單。
本篇博客轉(zhuǎn)載C語言中文網(wǎng)
總結(jié)
以上是生活随笔為你收集整理的高级线性表——静态链表(最全静态链表解读)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 线性表易错点与线性表程序设计易错点
- 下一篇: 使用循环链表解决约瑟夫环问题