聲明:數據結構與算法系列博文參考了《天勤高分筆記》、《王道復習指導》、C語言中文網。非商業用途,僅為學習筆記總結!
第一章《緒論》
一、基本概念及入門常識
////(一)數據結構的基本概念和術語//// 1. 數據 數據是信息的載體,是描述客觀事物屬性的數、字符以及所有能輸入到計算機中並被計算機程序識別和處理的符號的集合。 2. 數據元素 數據元素是數據的基本單位,通常作為一個整體進行考慮和處理。一個數據元素可由若干個數據項組成,數據項是構成數據元素的不可分割的最小單位。例如,學生記錄就是一個數據元素,它由學號、姓名、性別等數據項組成。 注意:不要混淆數據、數據元素、數據項之間的概念,也要注意和數據庫中的相關術語進行區別:如數據記錄、數據字段等概念。 3. 數據對象 數據對象是具有相同性質的數據元素的集合,是數據的一個子集。例如,整數數據對象是集合 N= {0,±1, 士2, ...}。 4. 數據類型 數據類型是一個值的集合和定義在此集合上一組操作的總稱。 1) 原子類型:其值不可再分的數據類型。 2) 結構類型:其值可以再分解為若千成分(分量)的數據類型。 3) 抽象數據類型:抽象數據組織和與之相關的操作。 5. 抽象數據類型 抽象數據類型(ADT)是指一個數學模型以及定義在該模型上的一組操作。抽象數據類型的定義僅取決於它的一組邏輯特性,而與其在計算機內部如何表示和實現無關,即不論其內部結構如何變化,只要它的數學特性不變,都不影響其外部的使用。通常用(數據對象、 數據關系、基本操作集)這樣的三元組來表示抽象數據類型。 6. 數據結構 在任何問題中,數據元素都不是孤立存在的,而是在它們之間存在着某種關系,這種數據元素相互之間的關系稱為結構(structure)。數據結構是相互之間存在一種或多種特定關系的數據元素的集合。數據結構包括三方面的內容:邏輯結構、存儲結構和數據的運算。數據的邏輯結構和存儲結構是密不可分的兩個方面,一個算法的設計取決於所選定的邏輯結構,而算法的實現依賴於所釆用的存儲結構。 ////(二)數據結構三要素:數據邏輯結構、數據存儲結構和數據的運算//// 1. 數據的邏輯結構 邏輯結構是指數據元素之間的邏輯關系,即從邏輯關系上描述數據。它與數據的存儲無關,是獨立於計算機的。數據的邏輯結構分為線性結構和非線性結構,線性表是典型的線性結構;集合、樹和圖是典型的非線性結構。數據的邏輯結構分類 集合結構中的數據元素之間除了 “同屬於一個集合”的關系外,別無其他關系。 線性結構結構中的數據元素之間只存在一對一的關系。 樹形結構結構中的數據元素之間存在一對多的關系。 圖狀結構或網狀結構結構中的數據元素之間存在多對多的關系。 2. 數據的存儲結構 存儲結構是指數據結構在計算機中的表示(又稱映像),也稱物理結構。它包括數據元素的表示和關系的表示。數據的存儲結構是邏輯結構用計算機語言的實現,它依賴於計算機語言。數據的存儲結構主要有:順序存儲、鏈式存儲、索引存儲和散列存儲。 1) 順序存儲:把邏輯上相鄰的元素存儲在物理位置上也相鄰的存儲單元里,元素之間的關系由存儲單元的鄰接關系來體現。其優點是可以實現隨機存取,每個元素占用最少的存儲空間;缺點是只能使用相鄰的一整塊存儲單元,因此可能產生較多的外部碎片。 2) 鏈接存儲:不要求邏輯上相鄰的元素在物理位置上也相鄰,借助指示元素存儲地址的指針表示元素之間的邏輯關系。其優點是不會出現碎片現象,充分利用所有存儲單元;缺點是每個元素因存儲指針而占用額外的存儲空間,並且只能實現順序存取。 3) 索引存儲:在存儲元素信息的同時,還建立附加的索引表。索引表中的每一項稱為索引項,索引項的一般形式是:(關鍵字,地址)。其優點是檢索速度快;缺點是增加了附加的索引表,會占用較多的存儲空間。另外,在增加和刪除數據時要修改索引表,因而會花費較多的時間。 4) 散列存儲:根據元素的關鍵字直接計算出該元素的存儲地址,又稱為Hash存儲。其優點是檢索、增加和刪除結點的操作都很快;缺點是如果散列函數不好可能出現元素存儲單元的沖突,而解決沖突會增加時間和空間開銷。 3. 數據的運算 施加在數據上的運算包括運算的定義和實現。運算的定義是針對邏輯結構的,指出運算的功能;運算的實現是針對存儲結構的,指出運算的具體操作步驟。 ////(三)算法的基本概念及特性(有窮性、確定性、可行性、輸入和輸出)//// 算法(algorithm)是對特定問題求解步驟的一種描述,它是指令的有限序列,其中每一條指令表示一個或多個操作。此外,一個算法還具有下列5個重要特性: 1) 有窮性 一個算法必須總是(對任何合法的輸入值)在執行有窮步之后結束,且每一步都可在有窮時間內完成。 2) 確定性 算法中每一條指令必須有確切的含義,讀者理解時不會產生二義性。即對於相同的輸入只能得出相同的輸出。 3) 可行性 一個算法是可行的,即算法中描述的操作都是吋以逋過已經實現的基本運算執行有限次來實現的。 4) 輸入 一個算法有零個或多個的輸入,這些輸入取自於某個特定的對象的集合。 5) 輸出 一個算法有一個或多個的輸出,這些輸出是同輸入有着某種特定關系的量。 通常設計一個“好”的算法應考慮達到以下目標: 正確性:算法應當能夠正確地解決求解問題。 可讀性:算法應當具有良好的可讀性,以助於人們理解。 健壯性:當輸入非法數據時,算法也能適當地做出反應或進行處理,而不會產生莫名其妙的輸出結果。 效率與低存儲量需求:效率是指算法執行的時間,存儲量需求是指算法執行過程中所需要的最大存儲空間,這兩者都與問題的規模有關。 ////算法效率度量:時間復雜度和空間復雜度//// 算法效率的度量是通過時間復雜度和空間復雜度來描述的。 時間復雜度 一個語句的頻度是指該語句在算法中被重復執行的次數。算法中所有語句的頻度之和記作T(n),它是該算法問題規模n的函數,時間復雜度主要分析T(n)的數量級。算法中的基本運算(最深層循環內的語句)的頻度與T(n)同數量級,所以通常釆用算法中基本運算的頻度 f(n)來分析算法的時間復雜度。因此,算法的時間復雜度也記為: T(n)=O(f(n)) 上式中“O”的含義是T(n)的數量級,其嚴格的數學定義是:若T(n)和f(n)是定義在正整數集合上的兩個函數,則存在正常數C和n0,使得當n>=n0時,都滿足0 <= T(n) <= C * f(n)。 注意:取f(n)中隨n增長最快的項將其系數置為1作為時間復雜度的度量。例如,fi(n) = a * n3 + b * n2 + c * n,則其時間復雜度為O(n3)。 算法的時間復雜度不僅依賴於問題的規模n,也取決於待輸入數據的性質(如輸入數據元素的初始狀態)。 例如:在數組A[0...n-1]中,查找給定值K的算法大致如下: i=n-1; while( i>=0 && (A[i]!=k) ) i--; // 語句(3) return i; 此算法中的語句(3)(基本運算)的頻度不僅與問題規模n有關,還與輸入實例中A 的各元素取值及K的取值有關: 若A中沒有與K相等的元素,則語句(3)的頻度 f(n)=n。 若A的最后一個元素等於K,則語句(3)的頻度f(n)是常數0。 最壞時間復雜度是指在最壞情況下,算法的時間復雜度。 平均時間復雜度是指所有可能輸入實例在等概率出現的情況下,算法的期望運行時間。 最好時間復雜度是指在最好情況下,算法的時間復雜度。 一般總是考慮在最壞情況下的時間復雜度,以保證算法的運行時間不會比它更長。 在分析一個程序的時間復雜性時,有以下兩條規則: a) 加法規則 T(n) = T1(n) + T2(n) = O(f(n)) + O(g(n)) = O(max(f(n), g(n))) b) 乘法規則 T(n) = T1(n) * T2(n) = O(f(n)) * O(g(n)) = O( f(n) * g(n) ) 常見的漸近時間復雜度有: O(1)<O(log2n)<O(n)<O(nlog2n)<O(n2)<O(n3)<O(2n)<O(n!)<O(nn) 空間復雜度 算法的空間復雜度S(n),定義為該算法所耗費的存儲空間,它是問題規模n的函數。漸近空間復雜度也常簡稱為空間復雜度,記作S(n)=O(g(n))。 一個上機程序除了需要存儲空間來存放本身所用指令、常數、變量和輸入數據外,也需要一些對數據進行操作的工作單元和存儲一些為實現計算所需信息的輔助空間,若輸入數據所占空間只取決於問題本身,和算法無關,則只需分析除輸入和程序之外的額外空間。 算法原地工作是指算法所需輔助空間是常量,即O(1)。
第二章《線性表》
一、概述
線性表:具有相同特性數據元素的有限序列
---相同特性:把同一類事物歸類,方便批量處理
---有限:表中元素個數為n,n有限大,n可以為0
---序列:表中元素排成一列,體現了一對一的邏輯特性(每個元素有且僅有一個前驅和一個后繼)
邏輯結構:只有一個表頭元素,只有一個表尾元素,表頭元素沒有前驅,表尾元素沒有后繼,除表頭表尾之后的其他元素只有一個前驅,也只有一個后繼。
存儲結構:①順序存儲結構(順序表);②鏈式存儲結構(單鏈表、雙鏈表、單循環鏈表、雙循環鏈表)
①順序存儲結構:利用數組的連續存儲空間順序存放線性表的各元素,每個結點包含所儲存元素的信息。
②鏈式存儲結構:不要求邏輯上相鄰的兩個元素物理上也相鄰;通過“鏈”建立起數據元素之間的邏輯關系。每個結點不僅包含所儲存元素的信息,還包含元素之間的邏輯關系信息;比如可以通過前驅結點中的地址信息找到后繼結點的位置。
特征對比:
1.在順序表中插入和刪除元素可能會導致移動大量元素的連帶操作(表尾位置除外),而鏈表不會;
2.在單鏈表中找到任意一個結點的位置不想順序表那么簡單,因為順序表支持隨機存取(存取),而單鏈表不支持;
3.為了彌補上一天單鏈表的不足,開發了雙鏈表、循環單鏈表和循環雙鏈表等存儲結構,這些存儲結構可以在僅知道鏈表中任意一個結點地址的情況下的推測其余 所有結點的地址,但仍然不支持隨機存取。
4.有時候還會給鏈表定義一個額外的指針,最常見的表尾指針,它指向鏈表中最后一個結點。可以借助它來提高某些常見操作的執行效率
5.線性表采用順序存儲結構,必須占用一片連續的存儲單元,而采用鏈式存儲結構則不需要這樣。
6.從整體來看,一般順序表存儲空間利用率低於鏈表;而從單個存儲單元來看,順序表存儲空間利用率要高於鏈表。
線性表的基本操作
一個數據結構的基本操作是指其最核心、最基本的操作。其他較復雜的操作可以通過調用其基本操作來實現。線性表的主要操作如下:
①InitList(&L):初始化表。構造一個空的線性表。
②Length(L):求表長度。返回線性表L的長度,即L中數據元素的個數。
③LocateElem(L, e):按值查找操作。在表L中查找具有給定關鍵字值的元素。
④GetElem(L, i):按位查找操作。獲取表L中第i個位置的元素的值。
⑤ListInsert(&L, i, e):插入操作。在表L中第i個位置上插入指定元素。
⑥ListDelete(&L, i, &e):刪除操作。刪除表L中第i個位置的元素。
⑦PrintList(L):輸出操作。按前后順序輸出線性表L的所有元素值。
⑧Empty(L):判空操作。若L為空表,則返回true,否則返回false。
⑨DestroyList(&L):銷毀操作。銷毀線性表,並釋放線性表L所占用的內存空間。
二、線性表的操作
(1)順序表

1、順序表的結構體定義
#define maxSize 50 //定義線性表的最大長度 typedef struct { int data[maxSize]; //順序表的元素(這里我們假設是int類型,也可能為其他類型) int length; //順序表的當前長度 }Sqlist; //順序表的類型定義
2、順序表的操作
①初始化 viod InitList(Sqlist &L) { L.length=0; } ②按值查找元素 //要求:用e獲取P位置上的元素 int GetElem(Sqlist L,int p,int &e) //e本身要發生變化,所以用引用型&e(從沒有元素到有元素) { if(p<0||p>L.length-1) return 0; e=L.data[p]; return 1; } ③按序號位置查找元素 //要求:查找元素e的序號 int LocateElem(Sqlist L,int e) { int i; for(i=0;i<L.length;i++) if(e==L.data[i]) return i; return -1; } ④插入元素 //要求:插入元素e int ListInsert(Sqlist &L,int p,int e) //L本身要發生變化,所以用引用型&L { int i; if(p<0||p>L.length||L.length==max.Size) //位置錯誤或者已經達到最大允許值,插入失敗 return 0; for(i=L.length-1;i>=p;--i) L.data[i+1]==L.data[i]; //采用先移動最右邊的元素;若先移動最左邊,右邊的元素會被覆蓋 L.data[p]=e; //將e賦值給p位置(成為p位置上的新元素) ++(L.length); //表內多了一個元素e,表長自增1 return -1; } ⑤刪除元素 //要求:刪除元素p,並把刪除的元素賦值給e int ListDelete(Sqlist &L,int p,int &e) //L、e本身要發生變化,所以用引用型&L,&e { int i; if(p<0||p>L.length-1) return 0; //將p位置上的元素賦值給e e=L.data[p]; for(i=p;i<L.length-1;++i) L.data[i]==L.data[i+1]; //被刪除元素右邊的所有元素都往左邊移動一個位置 --(L.length); return 1; //表內多了一個元素e,表長自減1 }
(2)單鏈表
概念:在每個結點中除了包含數據域外,還包含一個指針域,用以指向其后繼結點;這兩部分信息組成數據元素的存儲結構,稱之為“結點”。n個結點通過指針域相互鏈接,組成一個鏈表。


①帶頭結點:頭指針head指向頭結點,頭結點的值域不含任何信息,從頭結點的后繼結點開始存儲數據信息;頭指針始終不等於NULL,當head->next等於NULL的時候,鏈表為空。
②不帶頭結點:頭指針head直接指向開始結點;當head等於NULL的時候,鏈表為空
區分頭指針和頭結點:不論鏈表是否帶頭結點,頭指針都指向鏈表中第一個結點;而帶頭結點的鏈表中頭結點是第一個結點,只作為鏈表存在的標志,一般不儲存信息。

注:暫時默認操作的鏈表都帶頭結點,以后有時間再回頭總結不帶頭結點的相關操作。。。
<1>單鏈表結點定義
typedef struct LNode { int data; struct LNode *next; }LNode;
<2>查找結點
①按序號查找結點
在單鏈表中從第一個結點出發,順指針next域逐個往下搜索,直到找到第i個結點為止,否則返回最后一個結點指針域NULL。
LNode GetElem(LNode *C,int i)
LNode GetElem(LNode *C,int i) { //本算法取出單鏈表C(帶頭結點,L為頭結點)中第i個位置的結點指針 int j=1; //計數,初始為1 LNode *p; //定義指針p(指向結點) p= C; //指針P指向頭結點C(因為此時頭結點也是終端結點) if(i==0) return L; //若i等於0,則返回頭結點 if(i<1) return NULL; //若 i 無效,則返回 NULL while( p && j<i ) //從第1個結點開始找,查找第i個結點 { p=p->next; j++; } return p; //返回第i個結點的指針,如果i大於表長,p=NULL,直接返回p即可 } 時間復雜度:按序號查找操作的時間復雜度為O(n)
②按值查找表結點
從單鏈表第一個結點開始,由前往后依次比較表中各結點數據域的值,若某結點數據域的值等於給定值e,則返回該結點的指針。若整個單鏈表中沒有這樣的結點,則返回NULL。
LNode *LocateElem (LinkList L, ElemType e) { //本算法查找單鏈表 C(帶頭結點)中數據域值等於e的結點指針,否則返回NULL LNode *p; p=C; while( p!=NULL && p->data!=e) //從第1個結點開始查找data域為e的結點 p=p->next; return p; //找到后返回該結點指針,否則返回NULL } 時間復雜度:按值查找操作的時間復雜度為O(n)。
<3>插入結點
①后插操作(利用前驅結點):插入操作是將值為x的新結點插入到單鏈表的第i個位置上。先檢查插入位置的合法性,然后找到待插入位置的前驅結點,即第i-1個結點,再在其后插入新結點。

p=GetElem(L, i-1) ; //①查找插入位置的前驅結點 s->next=p->next; //②令新結點*s的指針域指向*p的后繼結點 p->next=s; //③令結點*p的指針域指向新插入的結點*s 注意:語句②③的順序不能顛倒,否則,當先執行p->next=s后,指向其原后繼的指針就不存在了,再執行s->next = p->next時,相當於執行了 s->next=s,顯然是錯誤的。 時間復雜度:本算法主要的時間開銷在於查找第i-1個元素,時間復雜度為O(n)。若是在給定的結點后面插入新結點,則時間復雜度僅為O(1)。
②前插操作(利用后繼結點):設待插入結點為*s,將插入到*p的前面。我們仍然將*s插入到*p的后面,然后將p->data與s->data交換

s->next = p->next; //①令新結點*s的指針域指向*p的后繼結點 p->next = s; //②令結點*p的指針域指向新插入的結點*s temp = p->data; //③把*p的指針域賦值給temp p->data=s->data; //④把*s的指針域賦值給*p (③④⑤為交換數據域部分) s->data=temp; //⑤把temp的指針域賦值給*s 時間復雜度:O(1)
<4>刪除結點
①刪除結點(利用前驅結點):先從鏈表的頭結點開始順序找到其前驅結點,然后再執行刪除操作。

p=GetElem(L,i-1); //①查找刪除位置的前驅結點 q=p->next; //②令q指向被刪除結點 p->next=q->next //③將*q結點從鏈中“斷開” free (q) ; //④釋放結點的存儲空間
時間復雜度:該算法的主要時間也是耗費在查找操作上,時間復雜度為O(n)
②刪除結點(利用后繼結點):用刪除*p的后繼結點操作來實現,實質就是將其后繼結點的值賦予其自身,然后刪除后繼結點,也能使得時間復雜度為O(1)

q=p->next; //①令q指向*p的后繼結點 p->data=p->next->data; //②和后繼結點交換數據域(刪除操作只考慮被留下來的數據即可) p->next=q->next; //③將*q結點從鏈中“斷開” free (q) ; //④釋放后繼結點的存儲空間
時間復雜度:O(1)
<5>創建鏈表
需求:n個元素已經儲存在數組a中,建立鏈表C
①尾插法
viod creatlistR(LNode *&C,int a[],int n) //要改變的變量用引用型//(R代表rear 尾巴) { LNode *s,*r; //定義指針s(指向新申請的結點);r(指向C的終端結點) C=(LNode*)malloc(sizeof(LNode)); //創建頭結點(申請C的頭結點空間) C->next=NULL; //初始化成空鏈表(鏈表取頭結點作為新鏈表的頭) r=C; //指針r指向頭結點C(因為此時頭結點也是終端結點) int i; for(i=0;i<n;++i) //循環申請n個結點來接收數組a[]中的元素 { s=(LNode*)malloc(sizeof(LNode)); //指針s指向新申請的結點 s->data=a[i]; //用新申請的結點來接收a數組中的一個元素 r->next=s; //用r來接納新結點 r=r->next; //r始終指向終端結點,以便接收下一個新來的結點 } r->next=NULL; //(r始終指向終端結點)r下一個設置為NULL,鏈表C建立完成 }
②頭插法
viod creatlistF(LNode *&C,int a[],int n) //(F代表first 第一個,開頭) { LNode *s; C=(LNode*)malloc(sizeof(LNode)); C->next=NULL; int i; for(i=0;i<n;++i) { s=(LNode*)malloc(sizeof(LNode)); s->data=a[i]; s->next=C->next; //s所指新結點的指針域next指向C中的開始結點 C->next=s; //頭結點的指針域next指向s結點,使得s成為新的開始結點(插入結點操作) } }
<6>求表長
求表長操作就是計算單鏈表中數據結點(不含頭結點)的個數,需要從第一個結點開始順序依次訪問表中的每一個結點,為此需要設置一個計數器變量,每訪問一個結點,計數器加1,直到訪問到空結點為止。算法的時間復雜度為O(n)。
注:因為單鏈表的長度是不包括頭結點的,因此,不帶頭結點和帶頭結點的單鏈表在求表長操作上會略有不同。對不帶頭結點的單鏈表,當表為空時,要單獨處理。
(3)雙鏈表
概念:單鏈表中只有一個指針,指向直接后繼,整個鏈表只能單方向從表頭訪問到表尾。如果算法中需要頻繁地找某結點的前驅結點,單鏈表的解決方式是遍歷整個鏈表,增加算法的時間復雜度,影響整體效率。
雙鏈表:在單向鏈表的基礎上,給各個結點額外配備一個指針變量,用於指向每個結點的直接前驅元素。
優點:雙鏈表可以很方便地找到其前驅結點,因此,插入、刪除結點算法的時間復雜度僅為O(1)


<1>雙鏈表結點定義
typedef struct DLNode //定義雙鏈表結點類型 { int data; //數據域 struct DLNode *prior; //指向直接前驅 struct DLNode *next; //指向直接后繼 }DLNode;
<2>插入結點
需求:1,2結點之間插入4結點(雙鏈表中p所指的結點之后插入結點*q)

p->next->prior=q; // ①把4變成2的前驅結點(新結點*q的指針域指向*p的后繼結點) q->next=p->next; // ②把2變成4的后繼結點(結點*p的指針域指向新插入的結點*s) q->prior=p; // ③把1變成4的前驅結點 p->next=q; // ④把4變成1的后繼結點 注:代碼的語句順序不是唯一的,但也不是任意的,①②兩步必須在④步之前,否則*p 的后繼結點的指針就丟掉了,導致插入失敗。
<3>刪除結點
雙鏈表刪除結點時,直接遍歷鏈表,找到要刪除的結點,然后利用該結點的兩個指針域完成刪除操作。
需求:刪除2結點(刪除雙鏈表中結點*p的后繼結點*q)

p->next=q->next; //①把3變成1的后繼結點 q->next->prior=p; //②把1變成3的前驅結點 free (q) ; //③釋放結點空間
建立雙鏈表的操作中,也可以采用如同單鏈表的頭插法和尾插法,但是在操作上需要注意指針的變化和單鏈表有所不同。
(4)循環單鏈表

在循環單鏈表中,表尾結點的next域指向L,故表中沒有指針域為NULL的結點,因此,循環單鏈表的判空條件不是頭結點的指針是否為空,而是它是否等於頭指針。
循環單鏈表的插入、刪除算法與單鏈表的幾乎一樣,所不同的是如果操作是在表尾進行,則執行的操作不相同,以讓單鏈表繼續保持循環的性質。當然,正是因為循環單鏈表是一個 “環”,因此,在任何一個位置上的插入和刪除操作都是等價的,無須判斷是否是表尾。
在單鏈表中只能從表頭結點開始往后順序遍歷整個鏈表,而循環單鏈表可以從表中的任一結點開始遍歷整個鏈表。有時對單鏈表常做的操作是在表頭和表尾進行的,此時可對循環單鏈表不設頭指針而僅設尾指針,從而使得操作效率更高。原因:若設置頭指針,則對表尾進行操作需要時間復雜度O(n);若設置尾指針r,則頭指針為r->next,即對表頭表尾操作的時間復雜度都為O(1)
(5)循環雙鏈表

在循環雙鏈表中,頭結點的prior 指針還要指向表尾結點。
在循環雙鏈表L中,某結點*p為尾結點時,p->next=L;當循環雙鏈表為空表時,其頭結點的prior域和next域都等於L。
(6)靜態鏈表

靜態鏈表是借助數組來描述線性表的鏈式存儲結構,結點也有數據域data和指針域 next,與前面所講的鏈表中的指針不同的是,這里的指針是結點的相對地址(數組下標),又稱為游標。和順序表一樣,靜態鏈表也要預先分配一塊連續的內存空間。
靜態鏈表和動態鏈表的區別:靜態鏈表限制了數據元素存放的位置范圍;動態鏈表是整個內存空間。數據元素只允許在這塊內存空間中隨機存放。
靜態鏈表結構體定義:
#define MaxSize 50 //靜態鏈表的最大長度 typedef struct{ //靜態鏈表結構類型的定義 ElemType data; //存儲數據元素 int next; //下一個元素的數組下標 }SLinkList[MaxSize];
靜態鏈表以next==-1作為其結束的標志。靜態鏈表的插入、刪除操作與動態鏈表相同,只需要修改指針,而不需要移動元素。總體來說,靜態鏈表沒有單鏈表使用起來方便,但是在一些不支持指針的高級語言(如Basic)中,這又是一種非常巧妙的設計方法。
下一篇預告:第三章《棧與隊列》
