靜態鏈表:線性存儲結構的一種,兼顧順序表和鏈表的優點,是順序表和鏈表的升級;靜態鏈表的數據全部存儲在數組中(順序表),但存儲的位置是隨機的,數據直接的一對一關系是通過一個整型變量(稱為“游標”,類似指針的功能)維持。
1. 靜態鏈表中的節點
數據域:用於存儲數據元素的值
游標:即數組下標,表示直接后繼元素所在數組中的位置
typedef struct { int data; //靜態鏈表節點中的數據 int cur; //靜態鏈表節點中的游標 }component;
例:使用靜態鏈表存儲數據元素4、5、6,過程如下:
注:通常靜態鏈表會將第一個數據元素放到數組下標為1(即a[1])的位置中。
圖中從a[1]存儲的數據元素4開始,通過存儲的游標變量3,可以在a[3]中找到元素4的直接后繼元素5;通過元素a[3]存儲的游標變量6,可在在a[6]中找到元素5的直接后繼元素6;這樣一直到某元素的游標變量為0截止(a[0]默認不存儲數據元素)
2. 備用鏈表
靜態鏈表中,除了數據本身通過游標組成鏈表外,還需要有一條連接各個空閑位置的鏈表,稱為備用鏈表。
作用:回收數組中未使用或者之前使用過(現在不用)的存儲空間,留待后期使用。即靜態鏈表使用數組申請的物理空間中,存在兩個鏈表,一條連接數據,另一條連接數組中為使用的空間。
注:通常備用鏈表的表頭位於數組下標為0(a[0])的位置,而數據鏈表的表頭位於數組下標為1(a[1])的位置。
靜態鏈表中設置備用鏈表的好處是,可以清楚地知道數組中是否有空閑位置,以便數據鏈表添加新數據時使用。比如,若靜態鏈表中數組下標為 0 的位置上存有數據,則證明數組已滿。
3 靜態鏈表的實現
在數據鏈表未初始化之前,數組中所有位置都處於空閑狀態,所以都鏈接在備用鏈表上。(圖1)
向靜態鏈表中添加數據時,需提前從備用鏈表中摘除結點,讓新數據使用。
備用鏈表摘除節點最簡單的方法是摘除a[0]的直接后繼節點(即摘除a[1]的游標2);同樣,向備用鏈表中添加空閑節點也是添加作為a[0]新的直接后繼節點(圖二中a[1]為a[0]新的直接后繼結點(游標為2)、圖三中a[2]為a[0]新的直接后繼結點(游標為3)、圖四中a[3]為a[0]新的直接后繼結點(游標為4))。因為 a[0] 是備用鏈表的第一個節點,我們知道它的位置,操作它的直接后繼節點相對容易,無需遍歷備用鏈表,耗費的時間復雜度為為O(1)
。
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 #define maxSize 6 5 6 typedef struct 7 { 8 int data; 9 int cur; //游標 10 }component; 11 12 /************************************************************************* 13 創建備用鏈表 14 *****************************************************************************/ 15 void reserveArr(component *array) 16 { 17 int i; 18 for(i=0;i<maxSize;i++) 19 { 20 array[i].cur=i+1; //將每個數組分量鏈接到一起 21 } 22 array[maxSize-1].cur=0; //鏈表最后一個節點的游標為0 23 } 24 25 /******************************************************** 26 提取分配空間 27 若備用鏈表為非空,則返回分配的節點下標,否則返回0(當分配最后一個節點時,該節點的游標值為0) 28 ********************************************************/ 29 int mallocArr(component *array) 30 { 31 int i=array[0].cur; 32 33 if(array[0].cur) 34 { 35 //array[0].data=0; 36 array[0].cur=array[i].cur; 37 } 38 return i; 39 } 40 41 /*************************************************************************** 42 初始化靜態鏈表 43 ****************************************************************************/ 44 int initArr(component *array) 45 { 46 int body,tempBody,i,j; 47 reserveArr(array); 48 body=mallocArr(array); 49 tempBody=body; //聲明一個變量把它當指針使,指向鏈表的最后一個節點,因為鏈表為空,所以和頭結點重合 50 for(i=1;i<4;i++) 51 { 52 j=mallocArr(array); //從備用鏈表中拿出空閑的分量 53 array[tempBody].cur=j;//將申請的空閑分量鏈接在鏈表的最后一個結點后面 54 array[j].data=i; //給新申請的分量數據域初始化 55 tempBody=j; //將指向鏈表最后一個結點的指針后移 56 } 57 array[tempBody].cur=0; //新的鏈表最后一個結點的指針設為0 58 return body; 59 } 60 61 void displayArr(component *array,int body) 62 { 63 int tempBody=body; //tempBody准備遍歷使用 64 while(array[tempBody].cur) 65 { 66 printf("%d,%d\n",array[tempBody].data,array[tempBody].cur); 67 tempBody=array[tempBody].cur; 68 } 69 printf("%d,%d\n",array[tempBody].data,array[tempBody].cur); 70 } 71 72 int main() 73 { 74 int body; 75 component array[maxSize]; 76 body=initArr(array); 77 printf("static link:\n"); 78 displayArr(array,body); 79 80 system("pause"); 81 return 0; 82 }
4 靜態鏈表中添加一個元素
void Insert(component *array,int body,int add,int a)//body鏈表頭結點在數組中的位置,add插入元素的位置,a插入的元素 { int tempBody=body; int i,insert; for(i=1;i<add;i++) { tempBody=array[tempBody].cur; } insert=mallocArr(array); array[insert].data=a; array[insert].cur=array[tempBody].cur; array[tempBody].cur=insert; }
5 靜態鏈表刪除元素
1.將存有目標元素的節點從數據鏈表中摘除;
2.將摘除節點添加到備用鏈表,以便下次再用;
//備用鏈表回收空間的函數,其中array為存儲數據的數組,k表示未使用節點所在數組的下標 void freeArr(component * array,int k)
{ array[k].cur=array[0].cur; array[0].cur=k; } //刪除結點函數,a 表示被刪除結點中數據域存放的數據 void deletArr(component * array,int body,char a)
{ int tempBody=body; //找到被刪除結點的位置 while (array[tempBody].data!=a)
{ tempBody=array[tempBody].cur; //當tempBody為0時,表示鏈表遍歷結束,說明鏈表中沒有存儲該數據的結點 if (tempBody==0)
{ printf("鏈表中沒有此數據"); return; } } //運行到此,證明有該結點 int del=tempBody; tempBody=body; //找到該結點的上一個結點,做刪除操作 while (array[tempBody].cur!=del)
{ tempBody=array[tempBody].cur; } //將被刪除結點的游標直接給被刪除結點的上一個結點 array[tempBody].cur=array[del].cur; //回收被摘除節點的空間 freeArr(array, del); }
6 靜態鏈表中查找元素
/******************************************************** 在以body作為頭結點的鏈表中查找數據域為elem的結點在數組中的位置 *****************************************************/ int SelectElem(component * array,int body,char elem){ int tempBody=body; while (array[tempBody].cur!=0) //當游標值為0時,表示鏈表結束 { if (array[tempBody].data==elem) { return tempBody; } tempBody=array[tempBody].cur; } return -1;//返回-1,表示在鏈表中沒有找到該元素 }
7 靜態鏈表中更改數據
/******************************************************** 在以body作為頭結點的鏈表中將數據域為oldElem的結點,數據域改為newElem **********************************************************/ void amendElem(component * array,int body,char oldElem,char newElem) { int add=selectElem(array, body, oldElem); if (add==-1) { printf("無更改元素"); return; } array[add].data=newElem; }