一、顺序表的基本概念与特点
- 定义:顺序表是一种线性表的存储结构,采用连续的内存空间存储数据元素
- 物理结构:逻辑上相邻的元素在物理内存中也相邻
- 优点:1.空间利用率高;2.支持随机访问;3.尾插、尾删效率极高(时间复杂度O(1))
- 缺点:1.头插、按位插入删除效率低;增容代价大
二、可扩容的顺序表的结构体设计
#define INIT_SIZE 10 //初始化时malloc购买的格子数量
//可扩容的顺序表的结构体设计
typedef int ELEM_TYPE;
typedef struct SeqList {
ELEM_TYPE* elem;//用来接收malloc返回的数组首地址
int length;//存放有效值个数
int listsize;//存放数组总的格子数
}SeqList,*PSeqList;
三、各功能函数的声明
//1.初始化
void Init_SeqList(struct SeqList* psl);
//2.插入数据(存入)
bool Insert_SeqList_head(PSeqList psl, ELEM_TYPE val);//头插
bool Insert_SeqList_tail(PSeqList psl, ELEM_TYPE val);//尾插
bool Insert_SeqList_pos(PSeqList psl, ELEM_TYPE val,int pos);//按位置插入
//3.删除数据(取)
bool Del_SeqList_head(SeqList* psl);//头删
bool Del_SeqList_tail(SeqList* psl);//尾删
bool Del_SeqList_pos(SeqList* psl,int pos);//按位置删除;默认pos=0,则为头删
bool Del_SeqList_val(SeqList* psl, ELEM_TYPE val);//按值删除;只删除val出现的第一次的位置
bool Del_SeqList_ALL_val_1(struct SeqList* psl, ELEM_TYPE val);//按值删除;只删除val出现的所有位置1
bool Del_SeqList_ALL_val_2(struct SeqList* psl, ELEM_TYPE val);//按值删除;只删除val出现的所有位置2
//4.查找数据是否已经存在(若存在,则只需要返回下标即可 找不到返回-1)
int Search_SeqList(PSeqList psl, ELEM_TYPE val);
//5.判空
bool Is_Empty(PSeqList psl);
//6.判满
bool Is_Full(PSeqList psl);
//7.扩容函数(1.5 2) 默认用2倍扩容
void Increase(PSeqList psl);
//8.清空 (不释放已购买的内存)
void Clear(PSeqList psl);
//9.销毁 (需要释放malloc购买的内存的)
void Destroy(PSeqList psl);
//打印
void show(PSeqList psl);
三、顺序表功能的实现方式
0、头文件的引用
#include <stdio.h>
#include <assert.h>
#include <stdlib.h> // malloc exit
#include "SeqList.h"
//#include <vld.h>//检测内存泄漏
1、初始化
//1.初始化
void Init_SeqList(struct SeqList* psl){
//0.安全处理 每一个函数都要写的
//assert(psl != NULL);
assert(psl != nullptr);
assert(nullptr != psl);
if (nullptr == psl){
exit(EXIT_FAILURE);
}
//1.对我们的 elem 用来去malloc 在堆区购买一块连续的空间
psl->elem = (ELEM_TYPE*)malloc(INIT_SIZE * sizeof(ELEM_TYPE));
if (NULL == psl->elem){
exit(EXIT_FAILURE);
}
//2.对我们的 length 初始化为0
psl->length = 0;
//3.对我们的 listsize 初始化为 INITSIZE
psl->listsize = INIT_SIZE;
}
2、插入
2.1头插
//头插
bool Insert_SeqList_head(PSeqList psl, ELEM_TYPE val) {
assert(nullptr != psl);
// 0.5判满,肯定有空余格子
if (Is_Empty(psl))Increase(psl);
//集体向后挪(尾巴先动)
for (int i = psl->length-1; i >= 0; i--) {
psl->elem[i + 1] = psl->elem[i];
}
//将val放入0号下标
psl->elem[0] = val;
//有效值个数+1
psl->length++;
return true;
}
2.2尾插
//尾插
bool Insert_SeqList_tail(PSeqList psl, ELEM_TYPE val) {
assert(nullptr != psl);
// 0.5判满
if (Is_Empty(psl))Increase(psl);
//找到尾巴
psl->elem[psl->length] = val;
//有效值个数+1
psl->length++;
return true;
}
2.3按位插入
//按位置插入;默认pos=0,则为头插
bool Insert_SeqList_pos(PSeqList psl, ELEM_TYPE val,int pos) {
assert(nullptr != psl);
assert(pos >= 0 && pos <= psl->length);
// 0.5判满
if (Is_Empty(psl))Increase(psl);
//将插入位置之后的元素,统一向后挪动,把插入位置空出来
for (int i = psl->length - 1; i >= pos; i--) {
psl->elem[i + 1] = psl->elem[i];
}
psl->elem[pos] = val;
//有效值个数+1
psl->length++;
return true;
}
3、删除
3.1头删
//头删
bool Del_SeqList_head(SeqList* psl) {
assert(nullptr != psl);
//0.5判空
if (Is_Empty(psl))return false;
//除了第一个元素之外,集体向前覆盖(尾巴先动)
for (int i = 1; i <psl->length; i++) {
psl->elem[i - 1] = psl->elem[i];
}
//有效值个数-1
psl->length--;
return true;
}
3.2尾删
//尾删
bool Del_SeqList_tail(SeqList* psl) {
assert(nullptr != psl);
//0.5判空
if (Is_Empty(psl))return false;
psl->length--;
return true;
}
3.3按位置删除
//按位置删除;默认pos=0,则为头删
bool Del_SeqList_pos(SeqList* psl, int pos) {
assert(nullptr != psl);
assert(pos >= 0 && pos <= psl->length);
// 0.5判空
if (Is_Empty(psl))return false;
//将删除位置之后的元素,统一向前挪动覆盖
for (int i = pos + 1;i<= psl->length - 1; i++) {
psl->elem[i - 1] = psl->elem[i];
}
psl->length--;
return true;
}
3.4按值删除;只删除val出现的第一次的位置
//按值删除;只删除val出现的第一次的位置
bool Del_SeqList_val(SeqList* psl, ELEM_TYPE val) {
assert(nullptr != psl);
//0.5判空
if (Is_Empty(psl))return false;
//通过调用函数,查找val值在顺序表的位置
int index = Search_SeqList(psl, val);
if (index == -1)return false;
return Del_SeqList_pos(psl, index);
}
3.5按值删除;只删除val出现的所有位置1
//按值删除;只删除val出现的所有位置1
bool Del_SeqList_ALL_val_1(struct SeqList* psl, ELEM_TYPE val) {
int count = 0;
for (int i = 0; i < psl->length; i++) {
if (psl->elem[i] == val) {
count++;
}
}
for(int i=0;i<count;i++){
int index = Search_SeqList(psl, val);
Del_SeqList_pos(psl, index);
}
return true;
}
3.6按值删除;只删除val出现的所有位置2
//按值删除;只删除val出现的所有位置2
bool Del_SeqList_ALL_val_2(struct SeqList* psl, ELEM_TYPE val) {
int qianfangkongxiangezishu = 0;
for (int i = 0; i < psl->length; i++) {
if (psl->elem[i] == val)
qianfangkongxiangezishu++;
else
psl->elem[i - qianfangkongxiangezishu] = psl->elem[i];
}
psl->length = psl->length - qianfangkongxiangezishu;
return true;
}
4、查找
//4.查找数据是否已经存在(若存在,则只需要返回下标即可 找不到返回-1)
int Search_SeqList(PSeqList psl, ELEM_TYPE val) {
for (int i = 0; i < psl->length; i++) {
if (psl->elem[i] == val) {
return i;
}
}
return -1;
}
5、判空
//5.判空
bool Is_Empty(PSeqList psl) {
return psl->length == 0;
}
6、判满
//6.判满
bool Is_Full(PSeqList psl) {
return psl->length == psl->listsize;
}
7、扩容
//7.扩容函数(1.5 2) 默认用2倍扩容
void Increase(PSeqList psl) {
ELEM_TYPE* tmp = (ELEM_TYPE * )realloc(psl->elem, psl->listsize * sizeof(ELEM_TYPE) * 2);
if (tmp != nullptr) {
psl->elem = tmp;
}
}
8、清空
//8.清空 (不释放已购买的内存)
void Clear(PSeqList psl) {
//malloc申请空间先不释放,而是认为所有格子里都是无效值
psl->length = 0;
}
9、销毁
//9.销毁 (需要释放malloc购买的内存的)
void Destroy(PSeqList psl) {
free(psl->elem);
psl->length = psl->listsize = 0;
}
10、打印
//打印
void show(PSeqList psl) {
//assert
for (int i = 0; i < psl->length; i++) {
printf("%d ",psl->elem[i]);
}
printf("\n");
}
四、关键操作与时间复杂度分析
- 插入操作:中间插入需移动后续元素,平均时间复杂度O(n)
- 删除操作:类似插入,需向前移动元素,平均时间复杂度O(n)
- 查找操作:按值查找O(n),按索引查找O(1)
五、顺序表的应用场景
- 适用于元素数量稳定、频繁访问但较少修改的场景
- 典型应用:数组排序、矩阵运算、哈希表底层实现
六、扩展内容:与其他结构的对比
- 与链表的比较:内存连续性、操作效率差异
- 与动态数组的关系:多数语言中动态数组(如C++的
vector
)基于顺序表实现