第三章 線性表之順序表
- 第三章 線性表之順序表
- 一、什么是線性表?
- 1> 概念
- 2> 線性表的基本操作
- 二、線性表的順序存儲
-
- 存儲結構
- 順序存儲圖示
-
- 核心操作
- 1> 初始化
- 順序表初始化圖示
- C 語言實現
- 2> 清空
- 順序表清空圖示
- C 語言實現
- 3> 銷毀
- 順序表銷毀圖示
- C 語言實現
- 4> 插入
- 順序表插入圖示
- C 語言實現
- 5> 刪除
- 順序表刪除圖示
- C 語言實現
- 6> 隨機訪問
- C 語言實現
- 總結
-
- 一、什么是線性表?
一、什么是線性表?
1> 概念
線性表: n 個元素的有序序列。n 可為 0,表示空表;n > 0 時,除了頭元素,每個元素都有后繼元素,除了尾元素,每個元素都有前驅元素。換句俗話,就是說線性表中有 n 個元素,它們按順序串成一串兒。
線性表的特點:
- 同一性:線性表的元素都屬于同一類型;
- 有窮性:線性表由有限個數據元素組成,表長度就是表中數據元素的個數;
- 有序性:線性表中元素有序。
2> 線性表的基本操作
線性表的抽象數據型規定了線性表的基本操作,它們相當于線性表的接口。基本操作只考慮功能,不考慮實現;不同的存儲方式下,實現相同的功能算法也不同;后續我們將采用不同的存儲方式實現這些基本操作。
- InitList(&L)
- 結果:構造一個空的線性表 L
- DestroyList(&L)
- 前提:線性表 L 已存在
- 結果:將 L 銷毀
- ClearList(&L)
- 前提:線性表 L 已存在
- 結果:將 L 置為空表
- EmptyList(&L)
- 前提:線性表 L 已存在
- 結果:如果 L 為空表,則返回 TRUE,否則,返回 FALSE
- ListLength(L)
- 前提:線性表 L 已存在
- 結果:返回表中的元素個數
- GetElem(L, i, &e)
- 前提:表 L 存在,且 0 <= i <= ListLength(L) - 1
- 結果:用 e 返回 L 中第 i 個數據元素
- LocateElem(L, e, compare())
- 前提:表 L 存在,compare 是數據元素的判定函數
- 結果:返回 L 中第 1 個與 e 滿足關系 compare() 的數據元素的位序,如果 e 不存在,在返回 0
- PriorElem(L, cur_e, &pre_e)
- 前提:線性表已存在
- 結果:若 cur_e 是 L 的數據元素,且不是第一個,則用 pre_e 返回它的前驅,否則操作失敗,pre_e 無定義
- NextElem(L, cur_e, &next_e)
- 前提:線性表已存在
- 結果:若 cur_e 是 L 的數據元素,且不是最后一個,則用 next_e 返回它的前驅,否則操作失敗,next_e 無定義
- ListInsert(&L, i, e)
- 前提:表 L 已存在,e 為合法元素值且 0 <= i <= ListLength(L)
- 結果:在 L 中第 i 個位置之前插入新的數據 e,L 的長度加 1
- ListDelete(&L, i, &e)
- 前提:表 L 存在且非空,0 <= i <= ListLength(L) - 1
- 結果:刪除 L 的第 i 個數據元素,并用 e 返回其值,L 的長度減 1
- ListTranverse(L, visit())
- 前提:表 L 存在
- 結果:依次對 L 的每個數據元素調用 visit(),一旦 visit() 失敗則操作失敗
二、線性表的順序存儲
線性表的順序存儲 是指用一組地址連續的存儲單元存儲數據,使得邏輯上相鄰的元素在物理上也是相鄰的。
1. 存儲結構
我們可以使用一維數組來存儲一個線性表的元素,具體如下:
// 緩沖區初始大小
#define LIST_INIT_SIZE 100
// 緩沖區增量
#define LIST_INCREMENT 10
// 順序表結構
typedef struct
{
char *elem; // 線性表存儲空間基址,為簡便起見,我們讓線性表存儲單個字符
int length; // 線性表中元素個數
int listsize; // 當前分配的存儲容量
} SeqList;
順序存儲圖示
順序表
2. 核心操作
我們詳細說明順序表的核心操作,其他操作見項目代碼。
1> 初始化
申請順序表空間,初始化變量。
順序表初始化圖示
順序表初始化
C 語言實現
// 前提:L 為未初始化的線性表
// 結果:將 L 初始化為空表
Status InitList(SeqList& L)
{ // 開辟順序表的初始存儲空間,即:緩沖區空間
L.elem = (char*) malloc(LIST_INIT_SIZE * sizeof(char));
if(!L.elem)
{ // 如果申請失敗,返回錯誤代碼
printf("存儲分配失敗\n");
return OVERFLOW;
}
L.length = 0; // 設置元素個數為 0
L.listsize = LIST_INIT_SIZE; // 設置初始緩沖區大小
return OK; // 初始化成功
}
2> 清空
將當前順序表中數據清除,保持原有存儲結構;此時緩沖區大小 L.listsize
不做改變,也就是說,緩沖區大小是可以大于初始大小 LIST_INIT_SIZE
的。
順序表清空圖示
順序表清空
C 語言實現
// 前提:線性表 L 已存在
// 結果:將 L 置為空表
void ClearList(SeqList &L)
{
// 將順序表緩沖區中的數據清零
if(L.elem)
memset(L.elem, 0, sizeof(char) * L.listsize);
// 將順序表長度置 0
L.length = 0;
}
3> 銷毀
將順序表銷毀,釋放所有占用的內存。
順序表銷毀圖示
順序表銷毀
C 語言實現
// 前提:線性表 L 已存在
// 結果:將 L 銷毀
void DestroyList(SeqList &L)
{
if(L.elem)
{
free(L.elem);
L.elem = NULL;
}
L.length = 0;
L.listsize = 0;
}
4> 插入
在順序表中的指定位置 i (0 <= i <= L.length) 插入元素 e。
順序表插入圖示
順序表插入
C 語言實現
// 前提:表 L 已存在,e 為合法元素值且 0 <= i <= ListLength(L)
// 結果:在 L 中第 i 個位置之前插入新的數據 e,L 的長度加 1,
// 插入成功返回 TRUE,否則,返回 FALSE
int ListInsert(SeqList &L, int i, char e)
{ // 判斷插入位置是否合法
if(i < 0 || i > L.length)
return ERROR; // 地址錯誤
// 判斷緩沖區是否充足
if(L.length >= L.listsize)
{ // 緩存區長度不夠,擴大容量,讓緩沖區增加 LIST_INCREMENT
char *buf = (char*) realloc(L.elem, (L.listsize + LIST_INCREMENT) * sizeof(char));
if(!buf) return OVERFLOW;
// 重置緩沖區指針
L.elem = buf;
// 重置緩沖區大小
L.listsize += LIST_INCREMENT;
}
// pos 指向下標為 i 的元素
char *pos = &(L.elem[i]);
// p 從最后一個數據元素 L.elem[L.length - 1] 開始,依次遞減,直到下標為 i 的元素
for(char *p = &(L.elem[L.length - 1]); p >= pos; p--)
*(p + 1) = *p; // 將當前元素向后移動一個位置
*pos = e; // 將新增元素放在 pos 所指位置
L.length++; // 元素個數加 1
return OK;
}
5> 刪除
將順序表中指定位置的元素刪除。
順序表刪除圖示
順序表刪除
C 語言實現
// 刪除表中元素
// 前提:表 L 存在且非空,0 <= i <= ListLength(L) - 1
// 結果:刪除 L 的第 i 個數據元素,并用 e 返回其值,L 的長度減 1
int ListDelete(SeqList &L, int i, char &e)
{
// 判斷刪除位置是否合法
if(i < 0 || i >= L.length) return ERROR;
// p 指向下標為 i 的元素
char *p = &L.elem[i];
// 將下標為 i 的元素放入 e 以便返回
e = *p;
// 從 p + 1 開始,到 L.elem[L.length - 1],將每個元素向前移動一個位置
for(p++; p < L.elem + L.length; p++)
*(p - 1) = *p;
L.length--; // 元素個數減 1
return OK;
}
6> 隨機訪問
獲取線性表中指定位置 i (0 <= i <= L.length - 1) 的元素值。
C 語言實現
// 前提:表 L 存在,且 0 <= i <= ListLength(L) - 1
// 結果:用 e 返回 L 中第 i 個數據元素,
// 如果失敗,返回 ERROR;否則,返回 OK
int GetElem(SeqList &L, int i, char *e)
{ // 判斷位置是否合法
if(i < 0 || i >= L.length)
return ERROR;
// 將下標為 i 的元素放入 e 以便返回
*e = L.elem[i];
return OK;
}
3. 總結
- 順序表隨機訪問時,效率比較高,直接就可以訪問到,時間為:O(1)
- 順序表插入和刪除時,為了保持順序結構,需要移動大量的元素,因此效率很差,時間復雜度為:O(n)