數據結構與算法(C/C++版)【緒論/線性表】

聲明:數據結構與算法系列博文參考了《天勤高分筆記》、《王道複習指導》、C語言中文網。非商業用途,僅爲學習筆記總結!算法

第一章《緒論》數據庫

1、基本概念及入門常識 數組

////(一)數據結構的基本概念和術語////
1. 數據
數據是信息的載體,是描述客觀事物屬性的數、字符以及全部能輸入到計算機中並被計算機程序識別和處理的符號的集合。
2. 數據元素
數據元素是數據的基本單位,一般做爲一個總體進行考慮和處理。一個數據元素可由若干個數據項組成,數據項是構成數據元素的不可分割的最小單位。例如,學生記錄就是一個數據元素,它由學號、姓名、性別等數據項組成。
注意:不要混淆數據、數據元素、數據項之間的概念,也要注意和數據庫中的相關術語進行區別:如數據記錄、數據字段等概念。
3. 數據對象
數據對象是具備相同性質的數據元素的集合,是數據的一個子集。例如,整數數據對象是集合 N= {01, 士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)。
#侃大山(密集恐懼症者慎入)0.0

 第二章《線性表》數據結構

1、概述 
線性表:具備相同特性數據元素的有限序列
  ---相同特性:把同一類事物歸類,方便批量處理
  ---有限:表中元素個數爲n,n有限大,n能夠爲0
  ---序列:表中元素排成一列,體現了一對一的邏輯特性(每一個元素有且僅有一個前驅和一個後繼)ide

邏輯結構:只有一個表頭元素,只有一個表尾元素,表頭元素沒有前驅,表尾元素沒有後繼,除表頭表尾以後的其餘元素只有一個前驅,也只有一個後繼。
存儲結構:①順序存儲結構(順序表);②鏈式存儲結構(單鏈表、雙鏈表、單循環鏈表、雙循環鏈表)函數

順序存儲結構:利用數組的連續存儲空間順序存放線性表的各元素,每一個結點包含所儲存元素的信息。
鏈式存儲結構:不要求邏輯上相鄰的兩個元素物理上也相鄰;經過「鏈」創建起數據元素之間的邏輯關係。每一個結點不只包含所儲存元素的信息,還包含元素之間的邏輯關係信息;好比能夠經過前驅結點中的地址信息找到後繼結點的位置。學習

特徵對比:spa

  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所佔用的內存空間。3d

 

2、線性表的操做 
(1)順序表 

一、順序表的結構體定義

#define maxSize 50 //定義線性表的最大長度
typedef struct
{
int data[maxSize]; //順序表的元素(這裏咱們假設是int類型,也可能爲其餘類型)
int length;        //順序表的當前長度
}Sqlist;           //順序表的類型定義

二、順序表的操做

①初始化
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)中,這又是一種很是巧妙的設計方法。

 

 

 下一篇預告第三章《棧與隊列》

相關文章
相關標籤/搜索