C语言顺序表技术详解:从原理到实现
一、引言
线性表(Linear List)是最基本、最简单且最常用的一种数据结构
有序性:元素之间存在严格的顺序关系
同类型:所有元素属于同一数据类型
分类:1. 顺序表
2.链表
- 线性表的概念与分类
- 顺序表的定义与核心特性
- 连续内存存储
- 随机访问能力 O(1)
- 顺序表在物理上和逻辑上都是连续的
- 适用场景分析
- 高频查询 and 低频插入/删除
二、顺序表的结构设计
- 静态分配实现
#define MAX_SIZE 100 typedef struct SeqList { int data[MAX_SIZE]; int length; } SeqList; - 动态分配实现(核心优势)
typedef int SLDataType; typedef struct SequenceList { SLDataType* arr; int size; int capasity; }SL; - 关键参数说明
静态分配实现存在局限性,如空间浪费或空间开辟不足的情况
动态分配则可以灵活的开辟空间,根据需要分配容量大小
capacityvsmax_size的区别- 内存布局示意图

三、核心操作实现

- 初始化与销毁
// 动态初始化 void SequenceList_Init(SL *ps) { ps->arr = NULL; ps->size = 0; ps->capasity = 0; } - 插入操作
- 时间复杂度分析: O(1) (尾部添加), O(n) (头部添加)
- 边界处理:位置合法性检查
//判断顺序表是否开辟空间 void SequenceList_Checkcapasity(SL *ps) { int newcapasity; if (ps->capasity == ps->size) { newcapasity = ps->capasity == 0 ? 4 : 2 * ps->capasity; SLDataType* temp = (SLDataType*)realloc(ps->arr, newcapasity * sizeof(SLDataType)); if (temp == NULL) { perror("realloc fail!"); exit(1); } ps->arr = temp; ps->capasity = newcapasity; } } //头部添加 void SequenceList_PushFront(SL* ps, SLDataType x) { assert(ps); SequenceList_Checkcapasity(ps); int i; for (i = ps->size; i>0; i--) { ps->arr[i] = ps->arr[i-1]; //arr[1] = arr[0],用来判断i的截止条件 } ps->arr[0] = x; ps->size++; } //尾部添加 void SequenceList_PushBack(SL* ps, SLDataType x) { assert(ps); SequenceList_Checkcapasity(ps); ps->arr[(ps->size)++] = x; } //指定地方添加 void SequenceList_Insert(SL* ps,int pos, SLDataType x) { assert(ps); assert(pos <= ps->size && pos >= 0); //位置合法性检查 SequenceList_Checkcapasity(ps); int i; for (i = ps->size; i>pos; i--) { ps->arr[i] = ps->arr[i - 1]; //arr[pos+1] = arr[pos],用来判断i的截止条件 } ps->arr[pos] = x; ps->size++; } - 删除操作
-
//头部删除 void SequenceList_PopFront(SL* ps) { assert(ps); int i; for (i = 0; i<ps->size -1; i++) { ps->arr[i] = ps->arr[i + 1]; //arr[size-2] = arr[size-1] } ps->size--; } //尾部删除 void SequenceList_PopBack(SL* ps) { assert(ps); (ps->size)--; } //指定地方删除 void SequenceList_Erase(SL* ps, int pos) { assert(ps); assert(pos <= ps->size && pos >= 0); int i; for (i = pos; i<ps->size-1; i++) { ps->arr[i] = ps->arr[i + 1]; //arr[size-2] = arr[size-1] } ps->size--; }
-
- 动态扩容策略
//判断顺序表是否开辟空间 void SequenceList_Checkcapasity(SL *ps) { int newcapasity; if (ps->capasity == ps->size) { newcapasity = ps->capasity == 0 ? 4 : 2 * ps->capasity; SLDataType* temp = (SLDataType*)realloc(ps->arr, newcapasity * sizeof(SLDataType)); if (temp == NULL) { perror("realloc fail!"); exit(1); } ps->arr = temp; ps->capasity = newcapasity; } }
四、性能与优化
- 时间复杂度对比表
操作 平均时间复杂度 随机访问 O(1) 尾部插入 O(1) 指定位置插入 O(n) 头部插入 O(n) - 批量操作优化策略
newcapasity = ps->capasity == 0 ? 4 : 2 * ps->capasity;
如果原先没有容量,则会先开辟一个大小为4个数据大小的空间
-
预分配机制
-
五、总结
- 顺序表的三大优势
由于缓存的速度相较于内存更快,所以cpu会将内存中的内容加载至缓存中再进行访问
若原先内容已经存在于缓存中,则直接访问,称为缓存命中
若原先内容不存在于缓存中,需要加载至缓存中再访问,称为不命中
由于缓存加载会将一块连续的区域加载至缓存,所以顺序表有CPU缓存友好性

直接通过下标即可访问
- CPU缓存友好性:
- 访问效率确定性
- 实现简单性
- 典型使用误区警示
- 避免中间频繁插入:效率低下
六、代码总览
Sequence.h
pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLDataType;
typedef struct SequenceList
{
SLDataType* arr;
int size;
int capasity;
}SL;
void SequenceList_Checkcapasity(SL* ps);//判断顺序表是否开辟空间
void SequenceList_Init(SL* ps);//初始化顺序表
void SequenceList_Print(SL* ps);//打印顺序表
void SequenceList_Destroy(SL* ps);//销毁顺序表
void SequenceList_PushFront(SL* ps, SLDataType x);//头部添加
void SequenceList_PushBack(SL* ps, SLDataType x);//尾部添加
void SequenceList_PopFront(SL* ps);//头部删除
void SequenceList_PopBack(SL* ps);//尾部删除
void SequenceList_Insert(SL* ps, int pos, SLDataType x);//指定地方添加
void SequenceList_Erase(SL* ps, int pos);//指定地方删除
Sequence.c
#define _CRT_SECURE_NO_WARNINGS
#include "Sequence.h"
//判断顺序表是否开辟空间
void SequenceList_Checkcapasity(SL *ps)
{
int newcapasity;
if (ps->capasity == ps->size)
{
newcapasity = ps->capasity == 0 ? 4 : 2 * ps->capasity;
SLDataType* temp = (SLDataType*)realloc(ps->arr, newcapasity * sizeof(SLDataType));
if (temp == NULL)
{
perror("realloc fail!");
exit(1);
}
ps->arr = temp;
ps->capasity = newcapasity;
}
}
//初始化顺序表
void SequenceList_Init(SL *ps)
{
ps->arr = NULL;
ps->size = 0;
ps->capasity = 0;
}
//打印顺序表
void SequenceList_Print(SL* ps)
{
assert(ps);
int i;
for (i = 0; i < ps->size; i++)
{
printf("%d ", ps->arr[i]);
}
printf("\n");
}
//销毁顺序表
void SequenceList_Destroy(SL* ps)
{
assert(ps);
if (ps->arr)
{
free(ps->arr);
}
ps->arr = NULL;
ps->capasity = ps->size = 0;
}
//头部添加
void SequenceList_PushFront(SL* ps, SLDataType x)
{
assert(ps);
SequenceList_Checkcapasity(ps);
int i;
for (i = ps->size; i>0; i--)
{
ps->arr[i] = ps->arr[i-1]; //arr[1] = arr[0]
}
ps->arr[0] = x;
ps->size++;
}
//尾部添加
void SequenceList_PushBack(SL* ps, SLDataType x)
{
assert(ps);
SequenceList_Checkcapasity(ps);
ps->arr[(ps->size)++] = x;
}
//头部删除
void SequenceList_PopFront(SL* ps)
{
assert(ps);
int i;
for (i = 0; i<ps->size -1; i++)
{
ps->arr[i] = ps->arr[i + 1]; //arr[size-2] = arr[size-1]
}
ps->size--;
}
//尾部删除
void SequenceList_PopBack(SL* ps)
{
assert(ps);
(ps->size)--;
}
//指定地方添加
void SequenceList_Insert(SL* ps,int pos, SLDataType x)
{
assert(ps);
assert(pos <= ps->size && pos >= 0);
SequenceList_Checkcapasity(ps);
int i;
for (i = ps->size; i>pos; i--)
{
ps->arr[i] = ps->arr[i - 1]; //arr[pos+1] = arr[pos]
}
ps->arr[pos] = x;
ps->size++;
}
//指定地方删除
void SequenceList_Erase(SL* ps, int pos)
{
assert(ps);
assert(pos <= ps->size && pos >= 0);
int i;
for (i = pos; i<ps->size-1; i++)
{
ps->arr[i] = ps->arr[i + 1]; //arr[size-2] = arr[size-1]
}
ps->size--;
}
test.c
#define _CRT_SECURE_NO_WARNINGS
#include "Sequence.h"
void Test01()
{
SL s;
SequenceList_Init(&s);
//尾部添加5个数据
SequenceList_PushBack(&s,11);
SequenceList_PushBack(&s, 22);
SequenceList_PushBack(&s, 33);
SequenceList_PushBack(&s, 44);
SequenceList_PushBack(&s, 55);
//头部添加1个数据
SequenceList_PushFront(&s, 66);
//打印顺序表
SequenceList_Print(&s);
//头部删除2个数据
SequenceList_PopFront(&s);
SequenceList_PopFront(&s);
SequenceList_Print(&s);
//指定地方添加一个数据
SequenceList_Insert(&s, 2, 77);
//打印顺序表
SequenceList_Print(&s);
//指定地方删除一个数据
SequenceList_Erase(&s, 2);
//打印顺序表
SequenceList_Print(&s);
//销毁顺序表
SequenceList_Destroy(&s);
}
int main()
{
Test01();
return 0;
}
1019

被折叠的 条评论
为什么被折叠?



