顺序表Sequence list
其实就是一个数组。
物理地址连续。但对比起一般数组来说,顺序表的数据必须是从第一个位置开始连续存储,紧挨着放,不能间隔放。
数据结构:帮助我们更好地管理数据——增删查改
基本结构SeqList
//定义在.h文件中。
typedef int SLDataType;
typedef struct SeqList{
SLDataType* a;//指向动态数组的指针。
int size;//顺序表的有效数据个数。
int capacity;//容量--空间大小。因为扩容时一般不会只扩一个,会一次扩多几个。
}SL;
检查容量CheckCapacity
当size和capacity相等时,有2种情况:一种是它们都为0,则此时直接给一个一定的容量,比如4,另一种是数据到达容量值时即size=capacity时,扩容2倍(也可以按其他规则扩容)。
void CheckCapacity(SL* ps){
assert(ps != NULL);
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 failed.\n");
exit(-1);//realloc失败一般就是没内存了。可以直接退出程序。这种情况遇到的比较少。
}
ps->a = tmp;
ps->capacity = newCapacity;
}
}
打印所有数据SLPrint
void SLPrint(SL* ps){
assert(ps != NULL);
for (int i = 0; i < ps->size; i++){
printf("%d ", ps->a[i]);
}
printf("\n");
}
0.3 销毁顺序表SLDestroy
void SLDestroy(SL* ps){
assert(ps != NULL);
if(ps->a){
free(ps->a);
ps->a = NULL;
ps->size = ps->capacity = 0;
}
}
初始化SLInit
void SLInit(SL* ps){
assert(ps != NULL);//防御式编程。防止他人不小心传个空指针过来。
ps->a = NULL;
ps->size = ps->capacity = 0;
}
尾插数据PushBack
void SLPushBack(SL* ps, SLDataType x){
assert(ps != NULL);
//插入数据x之前,要先检查容量空间
CheckCapacity(ps);
ps->a[ps->size] = x;
ps->size++;
}
尾删数据PopBack
void SLPopBack(SL* ps){
assert(ps != NULL);
//要对size进行检查,防止size变为负数,
//如果size变为负数,后续PushBack时会造成越界访问。
//注:越界的过程不一定会报错,但在SLDestroy的free时会报错。
assert(ps->size > 0);//当size等于0时,直接报错。
ps->size--;
}
//或者检查时温柔一点
void SLPopBack(SL* ps){
assert(ps != NULL);
if(ps->size == 0){
printf("The Sequence List is empty.\n");
return;
}
ps->size--;
}
头插数据PushFront
void SLPushFront(SL* ps, SLDataType x){
assert(ps != NULL);
CheckCapacity(ps);
//将所有数据往后挪动1个单位。
int end = ps->size - 1;
while (end >= 0){
ps->a[end + 1] = ps->a[end];
--end;
}
ps->a[0] = x;
ps->size++;
}
头删数据PopFront
向前覆盖
void SLPopFront(SL* ps){
assert(ps != NULL);
int begin = 1;
while(begin < ps->size){
ps->a[begin - 1] = ps->a[begin];
++begin;
}
ps->size--;
}
顺序表中,尾插尾删的时间复杂度为O(1),头插头删的时间复杂度是O(N)。
任意位置插入SLInsert
void SLInsert(SL* ps, int pos, SLDataType x){
assert(ps != NULL);
assert(pos >= 0 && pos <= ps->size);
SLCheckCapacity(ps);
//把pos位置后的所有数字往后挪一个单位。
int end = ps->size;
while (end >= pos){
ps->a[end] = ps->a[end - 1];
end--;
}
ps->a[pos] = x;
ps->size++;
}
实现了SLInsert,可以给PushBack和PushFront复用。
void SLPushBack(SL* ps, SLDataType x){
SLInsert(ps, ps->size, x);
}
void SLPushFront(SL* ps, SLDataType x){
SLInsert(ps, 0, x);
}
任意位置删除SLErase
void SLErase(SL* ps, int pos){
assert(ps != NULL);
assert(pos >= 0 && pos < ps->size);
//将pos后的数据往前挪动
int begin = pos;
while (begin < ps->size - 1){
ps->a[begin] = ps->a[begin + 1];
begin++;
}
ps->size--;
}
实现了SLErase,可以给PopBack和PopFront复用。
void SLPopBack(SL* ps){
SLErase(ps, ps->size - 1);
}
void SLPopFront(SL* ps){
SLErase(ps, 0);
}
查找SLSearch
int SLSearch(SL* ps, SLDataType x){
assert(ps != NULL);
for (int i = 0; i < ps->size; i++){
if (ps->a[i] == x){
return i;
}
}
return -1;
}
修改SLModify
void SLModify(SL*ps, int pos, SLDataType x){
assert(ps != NULL);
assert(pos >= 0 && pos < ps->size);
ps->a[pos] = x;
}
注意事项
越界不一定报错,例如以下程序在VS2013下就不会报错。
系统对越界的检查是设岗抽查,只检查一部分,比如下面的arr[10]就会报错。
int main(){
int arr[10];
//arr[10] = 1;
arr[12] = 1;
arr[15] = 1;
return 0;
}