?? 歡迎大家來到貝蒂大講堂??🎈🎈養成好習慣,先贊后看哦~🎈🎈
所屬專欄:數據結構與算法
貝蒂的主頁:Betty’s blog
1. 什么是順序表
順序表是用一段物理地址連續的存儲單元依次存儲數據元素的線性結構,它與數組非常類似,但是相比于數組順序表有一個非常明顯的優點——可以動態內存增長空間大小
2. 順序表的功能
順序表可以大致包含以下幾個功能:
初始化順序表中的數據。
打印順序表中的數據。
對順序表進行尾插(末尾插入數據)。
對順序表中數據進行尾刪(末尾刪除數據)。
3. 順序表的定義
定義順序表我們首先需要一個動態內存開辟的空間,當前數據的個數(size),以及方便擴容的容量大小(capacity)。
typedef int SLDataType; //類型重命名,方便后續修改類型
#define N 4 //初始化空間大小
typedef struct SeqList
{SLDataType* a; //指向動態開辟的數組size_t size; //有效數據個數size_t capacity; //容量大小
}SeqList;
4. 功能的具體實現
4.1 初始化順序表
(1) 代碼實現
初始化順序表時我們可以先開辟四個空間,之后不夠再進行擴容。
void SeqListInit(SeqList* p)
{assert(p); //斷言p->a =(SLDataType*)malloc(N*sizeof(SLDataType)); //初始順序表為四個空間if (p == NULL){perror("malloc fail:");return ;}p->size = 0; //初始數據個數為0p->capacity = 4; //初始空間容量為4
}
(2) 復雜度分析
- 時間復雜度:由于沒有其他未知數的引入,所以所需時間是個常數,也就是O(1)。
- 空間復雜度:動態開辟N個空間,所以空間復雜度為O(N)。
4.2 打印數據
(1) 代碼實現
打印數據只用循環打印就可以了。
void SeqListPrint(const SeqList* p)
{assert(p); //斷言if (p->size == 0) //判斷順序表是否為空{printf("順序表為空\n");return;}int i = 0;for (i = 0; i < p->size; i++) //打印順序表{printf("%d ", p->a[i]);}printf("\n");
}
(2) 復雜度分析
- 時間復雜度:因為會打印順序表中的所有數據,所以時間復雜度為O(N)。
- 空間復雜度:打印順序表并不需要開辟格外的空間,所以空間復雜度為O(N)。
4.3 尾插數據
尾插數據,顧名思義就是在順序表末尾插入數據,在插入數據之前我們應該檢查順序表是否為滿。
(1) 檢查是否已滿
void CheckCapacity(SeqList* p)
{assert(p); //斷言if (p->size == p->capacity) //檢查容量,滿了則增容{size_t newcapacity = 2 * p->capacity; //,擴容為原來的2倍SLDataType* prt = (SLDataType*)realloc(p->a, newcapacity * sizeof(SLDataType)); //擴容if (prt == NULL){perror("realloc fail:");return ;}p->a = prt; // prt不為空,開辟成功p->capacity = newcapacity; //更新容量}
}
(2) 插入數據
void SeqListPushBack(SeqList* p, SLDataType x)
{assert(p); //斷言CheckCapacity(p); //檢查順序表容量是否已滿p->a[p->size] = x; //尾插數據p->size++; //有效數據個數+1
}
(3) 復雜度分析
- 時間復雜度:沒有變量影響時間,時間復雜度為O(1)。
- 空間復雜度:以最壞的情況考慮,會進行擴容,空間復雜度為O(N)。
4.4 尾刪數據
同理,尾刪數據就是刪除順序表中最后一個元素,我們只需將size–。注意:刪除之前要保證順序表中有數據。
(1) 代碼實現
void SeqListPopBack(SeqList* p)
{assert(p); //斷言assert(p->size > 0); //順序表不能為空p->size--; //有效數據個數-1
}
(2) 復雜度分析
- 時間復雜度:沒有變量影響時間,時間復雜度為O(1)。
- 空間復雜度:沒有變量影響空間,空間復雜度為O(N)。
4.5 頭插數據
頭部插入數據就是在第一個元素位置插入一個元素。
(1) 代碼實現
void SeqListPushFront(SeqList* p, SLDataType x)
{assert(p); //斷言CheckCapacity(p); //檢查順序表容量是否已滿int i = 0;for (i = p->size - 1; i >= 0; i--) //順序表中[0,size-1]的元素依次向后挪動一位{p->a[i + 1] = p->a[i];}p->a[0] = x; //頭插數據p->size++; //有效數據個數+1
}
(2) 復雜度分析
- 時間復雜度:因為從頭部插入數據,后續數據需要依次覆蓋,所以時間復雜度為O(N)。
- 空間復雜度:因為可能會進行擴容,按最壞的情況來算,空間復雜度為O(N)。
4.5 頭刪數據
從頭部刪除數據,與尾部刪除數據不同,需要依次往前覆蓋。
(1) 代碼實現
void SeqListPopFront(SeqList* p)
{assert(p); //斷言assert(p->size > 0); //順序表不能為空int i = 0;for (i = 1; i < p->size; i++) //順序表中[1,size-1]的元素依次向前挪動一位{p->a[i - 1] = p->a[i];}p->size--; //有效數據個數-1
}
(2) 復雜度分析
- 時間復雜度:因為需要往前覆蓋數據,所以時間復雜度自然為O(N)。
- 空間復雜度:因為并沒有開辟其他空間,所以空間復雜度為O(1)。
4.6 查找指定值
根據輸入參數,在順序表中查找指定的值并返回其下標,若未找到則返回-1。
(1) 代碼實現
int SeqListFind(const SeqList* p, SLDataType x)
{assert(p); //斷言int i = 0;for (i = 0; i < p->size; i++){if (p->a[i] == x){return i; //查找到,返回該值在數組中的下標}}return -1; //沒有查找到
}
(2) 復雜度分析
- 時間復雜度:以最壞的情況分析,時間復雜度為O(N)。
- 空間復雜度:并沒有格外的空間消耗,空間復雜度為O(1)。
4.7 指定位置修改
我們可以通過指定下標或者查找指定值的下標來修改任意位置的值。
(1) 代碼實現
void SeqListModify(SeqList* p, int pos, SLDataType x)
{assert(p); //斷言assert(p->size > 0); //順序表不能為空assert(pos >= 0 && pos < p->size); //檢查pos下標的合法性p->a[pos] = x; //修改pos下標處對應的數據
}
(2) 復雜度分析
- 時間復雜度:因為是通過指定下標修改,所以時間復雜度為O(1)。
- 空間復雜度:沒有開辟額外的空間,所以空間復雜度為O(1)。
4.8 指定位置插入
和前面其他插入一樣,指定位置插入也需要判斷是否擴容。
(1) 代碼實現
void SeqListInsert(SeqList* p, int pos, SLDataType x)
{assert(p);//斷言assert(pos <= p->size); //檢查pos下標的合法性SeqListCheck(p);//擴容int cur = p->size;while (cur > pos){p->a[cur] = p->a[cur - 1];//覆蓋--cur;}p->a[pos] = x;p->size++;
}
(2) 復雜度分析
- 時間復雜度:需要依次覆蓋,時間復雜度為O(N)。
- 空間復雜度:可能需要擴容,空間復雜度為O(N)。
4.9 指定位置刪除
和前面的刪除差不多,但是指定刪除可能會依次覆蓋。
(1) 代碼實現
void SeqListErase(SeqList* p, int pos)
{assert(p); //斷言assert(p->size > 0); //順序表不能為空assert(pos >= 0 && pos < p->size); //檢查pos下標的合法性int i = 0;for (i = pos + 1; i < p->size; i++) //將pos位置后面的數據依次向前挪動一位{p->a[i - 1] = p->a[i];}p->size--; //有效數據個數-1
}
(2) 復雜度分析
- 時間復雜度:需要依次覆蓋,時間復雜度為O(N)。
- 空間復雜度:沒有額外的空間消耗,空間復雜度為O(1)。
4.10 回收空間
在結束操作之后,需要釋放開辟的空間,以防止內存泄漏。
(1) 代碼實現
void SeqListDestory(SeqList* p)
{assert(p); //斷言free(p->a); //釋放動態開辟的空間p->a = NULL; //置空p->size = 0; //數據個數置0p->capacity = 0; //空間容量大小置0
}
(2) 復雜度分析
- 時間復雜度:沒有額外的時間消耗,時間復雜度為O(1)。
- 空間復雜度:沒有額外的空間消耗,空間復雜度為O(1)。
5. 完整代碼
5.1 SeqList.h
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#define N 4 //初始化空間大小
typedef int SLDataType; //類型重命名,方便后續修改類型
typedef struct SeqList
{SLDataType* a; //指向動態開辟的數組size_t size; //有效數據個數size_t capacity; //容量大小
}SeqList;void SeqListInit(SeqList* p);//初始化順序表
void SeqListPrint(const SeqList* p);//打印順序表
void SeqListPushBack(SeqList* p, SLDataType x);//尾插
void SeqListPopBack(SeqList* p);//尾刪
void SeqListPushFront(SeqList* p, SLDataType x);//頭插
void SeqListPopFront(SeqList* p);//頭刪
int SeqListFind(const SeqList* p, SLDataType x);//查找數據
void SeqListModify(SeqList* p, int pos, SLDataType x);//修改數據
void SeqListInsert(SeqList* p, int pos, SLDataType x);//指定插入
void SeqListErase(SeqList* p, int pos);//指定刪除
void SeqListDestory(SeqList* p);//釋放空間
5.2 SeqList.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList.h"
void SeqListInit(SeqList* p)
{assert(p); //斷言p->a =(SLDataType*)malloc(N*sizeof(SLDataType)); //初始順序表為四個空間if (p == NULL){perror("malloc fail:");return ;}p->size = 0; //初始數據個數為0p->capacity = 4; //初始空間容量為4
}
void CheckCapacity(SeqList* p)
{assert(p); //斷言if (p->size == p->capacity) //檢查容量,滿了則增容{size_t newcapacity = 2 * p->capacity; //,擴容為原來的2倍SLDataType* prt = (SLDataType*)realloc(p->a, newcapacity * sizeof(SLDataType)); //擴容if (prt == NULL){perror("realloc");return ;}p->a = prt; // prt不為空,開辟成功p->capacity = newcapacity; //更新容量}
}
void SeqListPushBack(SeqList* p, SLDataType x)
{assert(p); //斷言CheckCapacity(p); //檢查順序表容量是否已滿p->a[p->size] = x; //尾插數據p->size++; //有效數據個數+1
}
void SeqListPrint(const SeqList* p)
{assert(p); //斷言if (p->size == 0) //判斷順序表是否為空{printf("順序表為空\n");return;}int i = 0;for (i = 0; i < p->size; i++) //打印順序表{printf("%d ", p->a[i]);}printf("\n");
}
void SeqListPopBack(SeqList* p)
{assert(p); //斷言assert(p->size > 0); //順序表不能為空p->size--; //有效數據個數-1
}
void SeqListPushFront(SeqList* p, SLDataType x)
{assert(p); //斷言CheckCapacity(p); //檢查順序表容量是否已滿int i = 0;for (i = p->size - 1; i >= 0; i--) //順序表中[0,size-1]的元素依次向后挪動一位{p->a[i + 1] = p->a[i];}p->a[0] = x; //頭插數據p->size++; //有效數據個數+1
}
void SeqListPopFront(SeqList* p)
{assert(p); //斷言assert(p->size > 0); //順序表不能為空int i = 0;for (i = 1; i < p->size; i++) //順序表中[1,size-1]的元素依次向前挪動一位{p->a[i - 1] = p->a[i];}p->size--; //有效數據個數-1
}
int SeqListFind(const SeqList* p, SLDataType x)
{assert(p); //斷言int i = 0;for (i = 0; i < p->size; i++){if (p->a[i] == x){return i; //查找到,返回該值在數組中的下標}}return -1; //沒有查找到
}
void SeqListModify(SeqList* p, int pos, SLDataType x)
{assert(p); //斷言assert(p->size > 0); //順序表不能為空assert(pos >= 0 && pos < p->size); //檢查pos下標的合法性p->a[pos] = x; //修改pos下標處對應的數據
}
void SeqListInsert(SeqList* p, int pos, SLDataType x)
{assert(p);//斷言assert(pos <= p->size); //檢查pos下標的合法性SeqListCheck(p);//擴容int cur = p->size;while (cur > pos){p->a[cur] = p->a[cur - 1];//覆蓋--cur;}p->a[pos] = x;p->size++;
}
void SeqListErase(SeqList* p, int pos)
{assert(p); //斷言assert(p->size > 0); //順序表不能為空assert(pos >= 0 && pos < p->size); //檢查pos下標的合法性int i = 0;for (i = pos + 1; i < p->size; i++) //將pos位置后面的數據依次向前挪動一位{p->a[i - 1] = p->a[i];}p->size--; //有效數據個數-1
}
void SeqListDestory(SeqList* p)
{assert(p); //斷言free(p->a); //釋放動態開辟的空間p->a = NULL; //置空p->size = 0; //數據個數置0p->capacity = 0; //空間容量大小置0
}