C语言顺序表
概念及结构
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。
在数组上完成数据的增删查改。
- 顺序表一般可以分为:
- 静态顺序表:使用定长数组存储元素。
- 动态顺序表:使用动态开辟的数组存储。
静态顺序表
- 代码实现
#define N 10 //这表示顺序表最多存储的数据
typedef int Datatype;
typedef struct SeqList
{
Datatype arr[N];//定长数组
int size;//数组中的有效数据个数
}SeqList;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
动态顺序表
- 代码实现
typedef int Datatype;
typedef struct SeqList
{
Datatype* arr;//指向动态开辟的数组
int size;//数组中的有效数据个数
int capicity;//容量空间的大小
}SeqList;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
接口实现(动态)
- 静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小,所以下面我们实现动态顺序表。
typedef int SLDateType;
typedef struct SeqList
{
SLDateType* a;//指向动态开辟的数组
int size;//数组中的有效数据个数
int capacity;
}SL;//容量空间的大小
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 需要完成的功能函数
//顺序表的初始化
void SeqListInit(SL* ps);
//顺序表销毁
void SeqListDestory(SL* ps);
//打印顺序表
void SeqListPrint(SL* ps);
//检查空间是否充足
void SeqListCheckCapacity(SL* ps);
//顺序表尾插
void SeqListPushBack(SL* ps, SLDateType x);
//顺序表头插
void SeqListPushFront(SL* ps, SLDateType x);
//头删
void SeqListPopFront(SL* ps);
//尾删
void SeqListPopBack(SL* ps);
// 顺序表查找
int SeqListFind(SL* ps, SLDateType x);
// 顺序表在pos位置插入x
void SeqListInsert(SL* ps, int pos, SLDateType x);
// 顺序表删除pos位置的值
void SeqListErase(SL* ps, int pos);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 功能实现
//顺序表的初始化
void SeqListInit(SL* ps)
{
ps->a = NULL;
ps->size = ps->capacity = 0;
}
//顺序表销毁
void SeqListDestory(SL* ps)
{
free(ps->a);
ps->a = NULL;
ps->capacity = ps->size = 0;
}
//顺序表打印
void SeqListPrint(SL* ps)
{
for (int i = 0; i < ps->size; ++i)
{
printf("%%d ", ps->a[i]);
}
printf("
");
}
//检查空间是否充足
void SeqListCheckCapacity(SL* ps)
{
// 如果没有空间或者空间不足,那么我们就扩容
if (ps->size == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
SLDataType* tmp = (SLDataType*)realloc(ps->a, newcapacity*sizeof(SLDataType));
if (tmp == NULL)
{
printf("realloc fail
");
exit(-1);
}
ps->a = tmp;
ps->capacity = newcapacity;
}
}
//顺序表尾插
void SeqListPushBack(SL* ps, SLDataType x)
{
SeqListCheckCapacity(ps);
ps->a[ps->size] = x;
ps->size++;
}
//顺序表头插
void SeqListPushFront(SL* ps, SLDataType x)
{
SeqListCheckCapacity(ps);
// 挪动数据
int end = ps->size - 1;
while (end >= 0)
{
ps->a[end + 1] = ps->a[end];
--end;
}
ps->a[0] = x;
ps->size++;
}
//顺序表尾删
void SeqListPopBack(SL* ps)
{
// 温柔处理方式
//if (ps->size > 0)
//{
// //ps->a[ps->size - 1] = 0;
// ps->size--;
//}
// 暴力处理方式
assert(ps->size > 0);
ps->size--;
}
//顺序表头删
void SeqListPopFront(SL* ps)
{
assert(ps->size > 0);
// 挪动数据
int begin = 1;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
++ begin;
}
ps->size--;
}
//查找
int SeqListFind(SL* ps, SLDataType x)
{
for (int i = 0; i < ps->size; i++)
{
if (ps->a[i] == x)
{
return i;
}
}
return -1;
}
// 顺序表在pos位置插入x
void SeqListInsert(SL* ps, int pos, SLDataType x)
{
assert(pos >= 0 && pos <= ps->size);
SeqListCheckCapacity(ps);
int end = ps->size - 1;
while (end >= pos)
{
ps->a[end + 1] = ps->a[end];
end--;
}
ps->a[pos] = x;
ps->size++;
}
// 顺序表删除pos位置的值
void SeqListErase(SL* ps, int pos)
{
assert(pos >= 0 && pos < ps->size);
int begin = pos + 1;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
begin++;
}
ps->size--;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
顺序表的优缺点
优点
- 支持随机访问。需要随机访问数据时,可以适应很好的算法。
缺点
- 头部、中部插入插入时间效率低。
- 连续的物理空间,空间不够了以后需要增容:
①增容有一定层度的消耗
②为了避免频繁的增容,一般我们按倍数去增容
这就倒置空间用不完时,会存在空间浪费。
推荐阅读