資料結構學習筆記(四)--順序表
資料結構學習筆記(四)--順序表
順序表是用順序儲存方式實現的線性表。
點選進入上一篇:資料結構學習筆記(三)--線性表的定義和基本操作
順序表的定義
順序表 --用順序儲存的方式實現的線性表
什麼是順序儲存
順序儲存:把邏輯上相鄰的元素儲存在物理位置上也相鄰的儲存單元中,元素之間的關係由儲存單元的鄰接關係來體現。
圖形表示
順序表的實現方式
靜態分配
定義一個靜態陣列存放資料元素。
程式碼實現
用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); //釋放原來的記憶體空間 }
順序表的特點
- 隨機訪問,即可以在O(1)時間內找到第i個元素。
- 儲存密度高,每個節點只儲存資料元素。
- 拓展容量不方便(即便採用動態分配的方式實現,拓展長度的時間複雜度也比較高)。
- 插入、刪除操作不方便,需要移動大量元素。
順序表的基本操作
順序表的初始化
見順序表的實現方式中的程式碼示例
順序表的插入
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語言程式設計》,那麼對語法要求就會更加嚴格。