順序表
一、簡介
順序表是在計算機內存中以數組的形式保存的線性表,是指用一組地址連續的存儲單元依次存儲數據元素的線性結構,邏輯結構與存儲位置吻合。
順序表分爲靜態順序表和動態順序表兩種。二者差異如下:
相同點:
內存空間連續, 數據順序存儲不同點:
(1)容量大小
靜態順序表再創建順序表的時候容量已經確定爲MAX_SIZE,不可以更改,而動態順序表的容量是由malloc函數動態開闢,當容量不夠用時可以增加容量capacity(2)二者所佔內存空間的位置不同。
靜態定義一個順序表, 順序表所佔的內存空間開闢在內存的靜態區, 即所謂的函數棧上, 隨着函數調用的結束, 這塊內存區域會被系統自動回收;而動態生成一個順序表, 順序表所佔的內存空間開闢在內存的動態區, 即所謂的堆內存上, 這塊區域不會隨着函數調用的結束被系統自動回收, 而是需要程序員主動去釋放它.
靜態、動態順序表優缺點:
靜態順序表:
操作簡單, 不用malloc函數,不用手動釋放其內存空間, 不存在內存泄漏
動態順序表:
可以動態開闢內存空間, 操作靈活, 相比於靜態開闢空間也可以少空間浪費
二、靜態順序表
1、結構定義
靜態順序表不可擴展空間,使用前需要定義最大空間大小MAX_SIZE。表結構定義需要記錄兩項數據:
- 順序表申請的存儲容量;
- 順序表的長度,也就是表中存儲數據元素的個數;
#define MAX_SIZE 100
#define ERROR 0
typedef int ElemType;
typedef struct SeqList {
ElemType data[MAX_SIZE];
int length;
}SeqList, *pSeqList;
2、初始化
void InitSeqList(SeqList *seq)
{
assert(nullptr != seq);
memset(seq->data, 0, sizeof(ElemType)*MAX_SIZE);
seq->length = 0;
}
3、插入元素
3.1 頭插
頭插,總是將新元素插入到順序表的首地址位置,需要將順序表的length個元素往後移動一個位置,時間複雜度O(n)。
void InsertFront(SeqList *seq, ElemType e)
{
assert(nullptr != seq);
if (seq->length == MAX_SIZE)
printf("SeqList is FULL!\n");
for (int i = seq->length; i > 0; --j)
seq->data[i] = seq->data[i-1];
seq->data[0] = e;
seq->length++;
}
3.2 尾插
尾插,總是從順序表的末尾插入,沒有元素的移動,時間複雜度O(1)
void InsertBack(SeqList *seq, ElemType e)
{
assert(nullptr != seq);
if (seq->length == MAX_SIZE)
{
printf("SeqList is FULL!\n");
return;
}
seq->data[seq->length++] = e;
}
3.3 任意指定位置插入
實現思路:
1.通過遍歷,找到數據元素要插入的位置
2.將要插入位置元素以及後續的元素整體向後移動一個位置
3.將元素放到騰出來的位置(即指定位置)
void Insert(SeqList seq, ElemType e, int pos)
{
assert(nullptr != seq);
if (seq->length == MAX_SIZE)
{
printf("SeqList is FULL!\n");
return;
}
if (seq->length == 0)
{
printf("SeqList is Empty!\n");
p = 1;
}
for (int i = seq->length; i >= pos; --i)
seq->data[i] = seq->data[i-1];
seq->data[p-1] = e;
seq->length++;
}
4、刪除元素
4.1 頭刪
總是從順序表的首地址開始刪除元素,將其後的length-1個元素都需要向前移動一位置, 時間複雜度爲O(n)
void DeleteFront(SeqList *seq)
{
assert(nullptr != seq);
if (seq->length == 0)
{
printf("SeqList is Empty!\n");
return;
}
for (int i = 0; i < seq->length; ++i)
{
seq->data[i] = seq->data[i+1];
}
seq->length--;
}
4.2 尾刪
尾刪,總是從順序表的尾部開始刪除元素,沒有元素的移動,時間複雜度爲O(1)
void DeleteBack(SeqList *seq)
{
assert(nullptr != seq);
if (seq->length == 0)
{
printf("SeqList is Empty!\n");
return;
}
seq->length--;
}
4.3 任意指定位置刪除
只需找到目標元素,並將其後續所有元素整體前移 1 個位置即可。(後續元素整體前移一個位置,會直接將目標元素刪除,可間接實現刪除元素的目的)
void Delete(SeqList *seq, int pos)
{
assert(nullptr != seq);
if (seq->length == 0)
{
printf("SeqList is Empty!\n");
return;
}
if (pos > seq->length || p < 1)
{
printf("ERROR!\n")
}
for (int i = pos-1; i < seq->length; ++i)
seq->data[i] = seq->data[i+1];
seq->length--;
}
5、查找
簡單粗暴的方法就是遍歷表數據,逐一進行比較判斷。實現如下:
int Find(SeqList *seq, ElemType value)
{
assert(nullptr != seq);
for (int i = 0; i < seq->length; ++i)
{
if (seq->data[i] == value)
return i;
}
return -1;
}
除此之外,可以對錶數據先進行排序,然後再進行二分查找等方式。
6、排序(升序)
// 排序
void BubbleSort(SeqList *seq)
{
assert(nullptr != seq);
for (int i = 1; i < seq->length; ++i)
{
for (j = 0; j < seq->length - i; ++j)
{
if (seq->data[j] > seq->data[j+1])
{
int tmp = seq->data[j];
seq->data[j] = seq->data[j+1];
seq->data[j+1] = tmp;
}
}
}
}
上述代碼是簡單等冒泡排序方式,當中還有可優化方案以及其它一些排序方法。
7、逆序反轉
void Reverse(SeqList *seq)
{
assert(nullptr != seq);
int start = 0;
int end = seq->length-1;
while (start != end)
{
int temp = seq->data[start];
seq->data[start] = seq->data[end];
seq->data[end] = temp;
start++;
end--;
}
}
三、動態順序表
相比與靜態順序表,動態順序表支持擴容機制。即動態開闢一塊新的空間(一般爲原空間的兩倍),將原空間的數據拷貝到新的空間,然後讓array指針指向新的空間並且釋放舊空間。
1、結構定義
#define LIST_INT_SIZE 100 //線性表存儲空間的初始分配量
#define LIST_INCREMENT 100 //線性表存儲空間的分配增量
typedef int ElemType;
typedef struct
{
ElemType *data; //存儲空間基址
int length; //當前長度
int capacity; //當前分配的存儲容量(等於靜態順序表MAX_SIZE)
}SqList;
2、初始化和銷燬
//初始化
void Init(SeqList *seq)
{
seq->capacity = LIST_INT_SIZE;
seq->data = (ElemType*)malloc(sizeof(ElemType) * seq->capacity);
assert(pSeq->parray);
pSeq->length = 0;
}
//銷燬
void Destroy(SeqList *seq)
{
free(seq->data);
pSeq->capacity = 0;
pSeq->data = nullptr;
pSeq->length = 0;
}
3、擴容判斷
void IsNeedExpand(SeqList *seq)
{
//擴容條件
if (seq->length < seq->capacity)
return;
//擴容
seq->capacity *= 2;
//1.申請新空間
ElemType *newData = (ElemType*)malloc(sizeof(ElemType) * seq->capacity);
assert(newData);
//2.數據搬移
for (int i = 0; i < seq->size; i++)
{
newData[i] = seq->data[i];
}
// 3. 釋放舊空間,關聯新空間
free(seq->data);
seq->data = newData;
}
4、其它操作
插入、刪除、查找等操作基本和靜態順序表差不多,額外需要注意等是所有的插入都需要擴容判斷處理。