1. 程式人生 > >資料結構啊----線性表知識點

資料結構啊----線性表知識點

資料結構這一塊,線性表是比較基礎和簡單的。但是我自己就在這一塊犯迷糊,特別是連結串列。好好學習學習。

1.線性表基本概念

1)定義:是由相同型別的結點組成的有限序列。如:n個結點組成的線性表

( a1, a2, an

a1是最前結點,an是最後結點。結點也稱為資料元素或者記錄。

2)線性表的長度:線性表中結點的個數稱為其長度。長度為0的線性表稱為空表

3)結點之間的關係:設線性表記為(a1,a2,ai-1 , ai, ai+1 ,an),稱ai-1ai的直接前驅結點(簡稱前驅),ai+1ai的直接後繼結點(簡稱後繼)。

4)線性表的性質:

① 線性表結點間的相對位置是

固定的,結點間的關係由結點在表中的位置確定。

② 如果兩個線性表有相同的資料結點,但它們的結點順序不一致,該兩個線性表也是不相等的。

注意:線性表中結點的型別可以是任何資料(包括簡單型別和複雜型別),即結點可以有多個成分,其中能唯一標識表元的成分稱為關鍵字(key),或簡稱鍵。以後的討論都只考慮鍵,而忽略其它成分,這樣有利於把握主要問題,便於理解。

線性表的基本概念還是比較簡單的。

特點是:

1.集合中存在唯一的一個第一元素 2.集合中存在唯一的一個最後元素 3.除最後元素之外,均有唯一的後繼 4.除第一元素外,均有唯一的前驅。

2.線性表的抽象資料型別

線性表是一個相當靈活的資料結構,其長度可以根據需要增加或減少。從操作上講,使用者不僅可以對線性表的資料元素進行訪問操作,還可以進行插入、刪除、定位等操作。

1)線性表的基本操作

假設線性表L有

資料物件 D={ai | ai∈ElemSet,i=1,2,3,,n,n>=0},

資料元素之間的關係R={<ai-1,ai>|ai-1,ai∈D,i=1,2,,n}

簡言之,一個線性表是n個數據元素的有限序列。

基本操作:    結構初始化操作    結構銷燬操作    引用型操作    加工型操作

2)線性表的操作舉例

① 用兩個線性表La,Lb分別表示兩個集合A、B,現要求兩個集合的合集,使得A=AUB。操作如下:依次取出Lb中的元素,然後到La中去找,如果找不到,則將該元素加入La中,同時修改La的長度,如果Lb中的元素同La中的元素相同,那麼按照集合的概念,不再加入到La中。演算法描述為:

        Void union(List &La , List Lb)

           {  La_len = length(La) ; Lb_len=length(Lb) ;

              for (i = 1 ; i <= Lb_len ; i++)

                 {  GetElem(Lb,i,e) ; //取出Lb的第i個元素,並將之賦值給e

                    if (!LocateElem(La,e,equal)) 

ListInsert(La,++La_len ,e) ;  

}   

}

② 有序線性表合併問題:利用抽象資料型別實現兩個線性表的合併

 已知線性表La和Lb中的資料元素按照非遞減有序排列,現在要求La和Lb歸併為一個新的有序線性表Lc,使得Lc仍然是非遞減有序排列。思想如下:

先設Lc為空表,從La、Lb的開頭開始,比較La、Lb當前兩個元素的大小,將較小者插入到Lc中。為了比較方便,我們輔設兩個指標i和j,讓它們分別指向La和Lb即將參與比較的元素。

將較小元素插入Lc後,該較小元素所在的線性表上輔設的指標向後移動一個位置(+1),另一個指標不變,繼續參與下一輪比較,這樣一直比到某一個線性表結束(i>La_length || j>Lb_length)。

 最後再將還沒有比較完的線性表中剩餘的元素全部插入Lc中即可。

 演算法如下:

         void MergeList(List La , List Lb , List &Lc)

          {

  InitList(Lc) ; 

  i=j=1 ; //兩個指標初始化,i指向La的第一個元素,j指向Lb的第一個元素

  k//用於儲存Lc當前元素個數,初始為0

  La_Length = length(La) ; Lb_Length= length(Lb);

  while (i<=La_Length && j<= Lb_Length)

    {

       GetElem(La,i,ai) ; 

       GetElem(Lb,j,bj);

       if (ai<=bj) 

{  ListInsert(Lc,++k ,ai) ;  

 i++ ;

}

else

 {  ListInsert(Lc,++k,bj) ; 

  j++;

}

}  //while

//LaLb中剩餘所有元素全部插入Lc中,以下兩句只可能執行一句。

while (i<=La_len) 

  { GetElem(La,i++ ,ai) ; ListInsert(Lc,++k,ai) };

While (j<= Lb_len)

  { GetElem(Lb,j++ , bj)} ; ListInsert(Lc,++k ,bj) ); 

二)線性表的實現

1.順序儲存結構

線性表有兩種儲存方式:順序儲存和鏈式儲存。順序儲存利用大陣列或分配了連續記憶體空間的指標實現,鏈式儲存利用連結串列實現。

1) 儲存方法:利用一個足夠大的陣列,從第一個元素開始將線性表的結點依次儲存在陣列中。我們知道,陣列是順序儲存的,利用陣列的目的是用陣列的順序儲存來體現線性表中結點的先後次序。由此得到的線性表稱為順序表,具有“隨機存取”的特點。

2) 地址表示及計算:線性表的順序儲存指的是用一組地址連續的儲存單元依次儲存線性表的資料元素。設每個元素佔用L個儲存單元,並以所佔的第一個單元的儲存地址作為資料元素的儲存位置,則第i+1個元素的儲存位置LOC(ai+1)和第i個元素的儲存位置LOC(ai)有如下關係:

                 LOC(ai+1) = LOC(ai) +L

假設LOC(a1)是線性表第一個資料元素a1的儲存位置(線性表的起始位置),則資料元素ai的地址計算公式為:

                 LOC(ai) = LOC(a1) + (i-1)×L

假設m>n,則有:

                 LOC(am) = LOC(an) + (m-n)×L

3) 線性表的順序儲存結構定義:

define List_INIT_SIZE 100    //初始分配量

# define LISTINCREMENT 10      //分配增量

typedef struct

  {  ElemType *elem ; //帶有連續地址塊的指標變數,相當於一維陣列(向量)

int length//x線性表的當前長度,即當前資料元素的個數,初始值為0

      int ListSize ; //線性表當前分配的儲存容量(以sizeof(ElemType)為單位)

SqList

線性表初始化時,利用下面的語句為指標成員elem分配連續地址空間:

        L.elem= (ElemType *malloc(LIST_INIT_SIZE *sizeof(ElemType)) ;

4) 順序表的各種操作

① 初始化線性表

Status  Init_Sq(SqList &L)

{  //構造一個空的線性表

L.elem = (ElemType * ) malloc( List_INIT_Size * sizeof(ElemType) ) ;

if (!L.elem) exit(OVERFLOW) ;  //儲存分配失敗

L.length = 0 ;

L.ListSize = List_INIT_Size ;

return OK ;

}

重要說明:

  • elem為指標變數。在堆上分配了記憶體空間的指標變數相當於一個數組,而elem則變為指向陣列基地址的指標變數,可用elem[i]來表示陣列的第i個元素。(elem可看成是陣列的名字)

  • !L.elem也可寫成L.elem == NULL

  • 之所以用給指標變數分配記憶體空間的方法定義陣列,是因為線性表在使用時很可能是動態的。如果用陣列定義的話,其長度是固定的,不利於動態使用,缺少靈活性。

    ② 在第i個元素之前插入一個新元素

    需要將第i個元素到第n個元素均向後移動一個單位,插入的新元素成為第i個元素,原來的第i個元素成為第i+1個元素,原來的第n個元素成為第n1個元素,線性表的長度加1

    Status listInsert_Sq(Sqlist &L,inti , ElemType e)

    {  // i的合法取值範圍是:1<=i<=ListLength(L) + 1

    if (i<1 || i > L.length +1)return ERROR ;

    if (L.length >= L.ListSize)    //如果儲存空間已滿,則增加分配

    {  newbase = (ElemType *) realloc(L.elem,

     (L.ListSize + LISTINCREMENT)*sizeof(ElemType)) ;

    if (!newbase) exit(OVERFLOW) ;  //儲存分配失敗

    L.elem = newbase ; //新基址

    L.ListSize += LISTINCREMENT ;  //增加儲存容量

    }

    q = &(L.elem[i-1]) ; // q為插入位置

    for ( p = &(L.elem[L.length-1]); p>=q ; --p)   *(p+1) = *p ;

                     //插入位置以及以後的元素右移

    *q = e  ; //插入e

    ++L.length ; //表的長度增1

    return OK ;

    }  

    重要說明:

  • 插入操作的主要工作都放在移動元素上。假設線性表中含有n個數據元素,在進行插入操作時,若假定在n+1個位置上插入元素的可能性均等,則平均移動元素的個數為:

  • 除非插入線上性表的最後,否則都要移動元素。

    ③ 刪除第i個元素,並返回其值

    Status ListDelete_Sq(SqList &L , inti , ElemType &e)

    {  if ((i<1) || (i>L.length)) return ERROR ;

    P= &(L.elem[i-1]) ;

    e = * p ;

    q = L.elem + L.length  1 ; //表尾元素位置

    for (++p ; p<= q ; ++p)

    *(p-1) = *p ; //被刪除元素之後的所有元素左移

    --L.length;

    return OK ;

    }

    重要說明:

  • 在進行刪除操作時,若假定刪除每個元素的可能性均等,則平均移動元素的個數為:

5) 順序儲存方式的特點:

優點:能直接訪問線性表的任一結點,訪問時間幾乎不受結點儲存位置的影響,這就是所謂的“隨機存取”機制。

缺點:① 陣列長度固定,動態性太差;

② 執行結點插入、刪除操作將移動大量資料。

6) 重要結論:

  • 一般情況下,在順序表的第i1<=i<=n)個元素之前插入一個元素,需要將第ni的元素(共n-i+1個元素)向後移動一個位置。

  • 一般情況下,刪除順序表的第i1<=i<=n)個元素時需要將第i+1到第n個元素(共n-i個元素)依次向前移動一個位置。

  • 在順序表中插入或者刪除一個數據元素,平均約需移動一半元素,設表長為n,則插入演算法和刪除演算法的時間複雜度為O(n)

也很簡單其實。線性表的順序儲存沒有什麼難度。主要就是操作的時候表的長度。

-----END