1. 程式人生 > 其它 >資料結構學習筆記(四)--順序表

資料結構學習筆記(四)--順序表

資料結構學習筆記(四)--順序表

順序表是用順序儲存方式實現的線性表。

點選進入上一篇:資料結構學習筆記(三)--線性表的定義和基本操作

順序表的定義

順序表 --用順序儲存的方式實現的線性表

什麼是順序儲存

順序儲存:把邏輯上相鄰的元素儲存在物理位置上也相鄰的儲存單元中,元素之間的關係由儲存單元的鄰接關係來體現。

圖形表示

順序表的實現方式

靜態分配

定義一個靜態陣列存放資料元素。

程式碼實現

用c語言舉例:

#include <stdio.h>
#define MaxSize 10 //定義最大長度
typedef struct{
    in data[MaxSize]; //用靜態的“陣列”存放資料元素
    int length; //順序表的當前長度
} SqList; //順序表的型別定義(靜態分配方式),自定義命名

//基本操作 ——初始化一個順序表
void InitList(SqList &L){
    for(int i=0;i<MaxSize;i++)
        L.data[i] = 0; //將所有資料元素設定為預設初始值
    L.length = 0; //順序表初始長度為0
}

注:如果沒有初始化資料結構,直接列印順序表,打印出來的資料未知(記憶體中的遺留資料,也稱髒資料)

靜態分配的特性

順序表的表長開始確定後無法更改(儲存空間是靜態的)

動態分配

使用指標相關知識和c語言malloc、free函式動態申請和釋放記憶體空間。

程式碼實現

用c語言舉例:

#include <stdlib.h>
#define InitSize 10 //預設的最大長度
typedef struct{
    int *data; //指示動態分配陣列的指標
    int MaxSize; //順序表的最大容量
    int length; //順序表的當前長度
} SeqList;

//基本操作 ——初始化一個順序表
void InitList(SeqList &L){
    //用malloc函式申請一片連續的儲存空間
    L.data = (int *)malloc(InitSize * sizeof(int)); //注意型別轉換
    L.length = 0;
    L.MaxSize = InitSize; //初始化順序表最大長度
}

//基本操作 ——增加動態陣列的長度
void IncreaseSize(SeqList &L,int len){
    int *p = L.data; //建立一個指標將指向原data資料所在記憶體,類似於一個temp;
    L.data = (int *)malloc((L.MaxSize+len)*sizeof(int));//給data分配一個新的,增大後的記憶體空間
    //遍歷將原來的資料賦值給新的地址(時間開銷大)
    for(int i=0;i<L.length;i++){
        L.data[i] = p[i];  //將原data資料賦值給新的data
    }
    L.MaxSize = L.MaxSize +Len; //順序表最大長度增加 len,重新定義順序表最大長度
    free(p); //釋放原來的記憶體空間
}

順序表的特點

  1. 隨機訪問,即可以在O(1)時間內找到第i個元素。
  2. 儲存密度高,每個節點只儲存資料元素。
  3. 拓展容量不方便(即便採用動態分配的方式實現,拓展長度的時間複雜度也比較高)。
  4. 插入、刪除操作不方便,需要移動大量元素。

順序表的基本操作

順序表的初始化

順序表的實現方式中的程式碼示例

順序表的插入

ListInsert(&L,i,e):插入操作。在表中第i個位置上插入指定元素e。

程式碼實現

採用靜態分配的儲存方式,用c語言舉例:

bool ListInsert(SqList &L,int i,int e){
    //首先判斷引數是否合理,保持程式碼的健壯性
	if(i<1 || i>L.length+1) //判斷i的範圍是否有效
    	return false;
    if(L.length>=MaxSize)
        return false;
    for(int j=L.length;j>=1;j--) //將第i個元素及之後的元素後移
        L.data[j] = L.data[j-1];
    L.data[i-1] = e; //在指定位置i處放入e。(因為線性表是位序從1開始,程式碼陣列下標從0開始,故為i-1)
    L.length++; //長度加1
    return true;
}

插入操作的時間複雜度

  • 最好情況:新元素插入到表尾,不需要移動元素,i = n+1,迴圈0次;最好時間複雜度 = O(1)
  • 最壞情況:新元素插入到表頭,需要將原有的n 個元素全部向後移動,i = 1,迴圈n次;最壞時間複雜度 = O(n)
  • 平均情況:假設新元素插入到任何一個位置的概率相同,即i = 1,2,3, ... ,length+1 的概率都是 p = 1/(n+1),計算構成等差數列求得,T(n) = n/2;平均時間複雜度 = O(n)

順序表的刪除

ListDelete(&L,i,&e):刪除指定位序上的元素,並用e帶回刪除資料的值。

程式碼實現

採用靜態分配的儲存方式,用c語言舉例:

bool ListDelete(SqList &L,int i,int &e){
    if(i<1 || i>L.length) //判斷i的範圍是否有效
        return false;
    e = L.data[i-1]; //將被刪除的元素賦值給e
    for(int j=i;j<L.length;j++) //將第i個位置後的元素前移
        L.data[j-1]=L.data[j];
    L.length--; //線性表長度-1
    return true;
}

刪除操作的時間複雜度

  • 最好情況:刪除表尾元素,不需要移動其他元素,i = n,迴圈0次;最好時間複雜度 = O(1)
  • 最壞情況:刪除表頭元素,需要將後續的n-1個元素全部向前移動,i = 1,迴圈 n-1次;最壞時間複雜度 = O(n)
  • 平均情況:假設刪除任何一個元素的概率相同,即i = 1,2,3, ... ,length 的概率都是 p = 1/n,計算構成等差數列求得,T(n) = (n-1)/2;平均時間複雜度 = O(n)

注意事項

刪除位序為i的元素和刪除陣列下標為i的元素並不相同,前者從1開始,後者從0開始。

順序表的查詢

按位查詢

GetElem(L,i):按照順序表位序查詢資料的值,並返回資料的值。

程式碼實現

採用靜態分配的儲存方式,用c語言舉例:

int GetElem(SqList L,int i){
    return L.data[i-1]; //直接返回該位序所在陣列下標的值
}

注:因呼叫malloc函式的記憶體分配和定義陣列的記憶體分配方式相似,故動態分配與靜態分配的按位查詢都是如此,以查詢陣列下標的形式實現。

時間複雜度

按位查詢的時間複雜度:O(1) (體現順序表“隨機存取”的特性)

按值查詢

LocateElem(L,e):在表中查詢具有給定關鍵字值的元素,並返回其位序

程式碼實現

採用靜態分配的儲存方式(當然,動態也是這麼做),用c語言舉例:

int LocateElem(SqList L,int e){
    for(int i=0;i<L.length;i++){
        if(L.data[i]==e)
            return i+1; //陣列下標為i的元素值等於e,返回其位序i+1
    }
    return 0; //迴圈結束還沒找到,說明查詢失敗
}
時間複雜度
  • 最好情況:目標元素在表頭,迴圈1次;最好時間複雜度 = O(1)
  • 最壞情況:目標元素在表尾,迴圈n次;最壞時間複雜度 = O(n)
  • 平均情況:假設目標元素出現在任何一個位置的概率相同,都是1/n,平均迴圈次數 = (n+1)/2,平均時間複雜度 = O(n)
注意事項

《資料結構》考研初試中,手寫程式碼可以直接用 “==”,無論e的資料型別是基本資料型別還是結構型別。

手寫程式碼主要考察學生是否能理解演算法思想,不會嚴格要求程式碼完全可執行。

但如果有的學校考《C語言程式設計》,那麼對語法要求就會更加嚴格。