线性表
线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串…线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
什么是顺序表呢?
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
顺序表分为静态顺序表和动态顺序表,我们重点讲动态的,因为实用性更大。
静态顺序表
使用定长的数组来存储数据。
// 静态 #define MAX 100 typedef int SLDateType; typedef struct SeqList { SLDateType arr[MAX]; int size; // 有效数据的个数 }SL;
动态顺序表
使用动态开辟的数组存储。
// 动态 typedef int SLDateType; typedef struct SeqList { SLDateType* arr; int size; // 有效数据的个数 int capacity; // 容量空间的大小 }SeqList;
接口实现
那么有哪些接口呢?
// 对数据的管理:增删查改 // 顺序表的初始化 void SeqListInit(SeqList* ps); // 顺序表销毁 void SeqListDestroy(SeqList* ps); // 顺序表打印 void SeqListPrint(SeqList* ps); // 顺序表尾插 void SeqListPushBack(SeqList* ps, SLDateType x); // 顺序表头插 void SeqListPushFront(SeqList* ps, SLDateType x); // 顺序表头删 void SeqListPopFront(SeqList* ps); // 顺序表尾删 void SeqListPopBack(SeqList* ps); // 顺序表查找 int SeqListFind(SeqList* ps, SLDateType x); // 顺序表在pos位置插入x void SeqListInsert(SeqList* ps, int pos, SLDateType x); // 顺序表删除pos位置的值 void SeqListErase(SeqList* ps, int pos);
接下来我们来一一的实现这些接口。
初始化
我们这里主要讲的是动态的顺序表,我们假定刚开始给顺序表设置为4个元素,不够用我们就扩容。
//初始化 void SeqListInit(SeqList* ps) { //防止ps为空指针 assert(ps); ps->arr = (SLDateType*)malloc(sizeof(SLDateType) * 4); //判断是够开辟成功 if (ps->arr == NULL) { perror("malloc falled"); return; } ps->size = 0; ps->capacity = 4; }
打印
打印比较简单,我们只需要遍历一遍顺序表就可以了。
// 打印顺序表 void SeqListPrint(SeqList* ps) { assert(ps); for (int i = 0; i < ps->size; i++) { printf("%d ", ps->arr[i]); } printf("\n"); }
销毁顺序表
我们需要将动态开辟的内存释放了,然后将数据和容量都置为0.
// 销毁顺序表 void SeqListDestroy(SeqList* ps) { assert(ps); free(ps->arr); ps->size = 0; ps->capacity = 0; }
尾删
尾删只需要将有效数据的个数减少一下就可以了,但是在这之前要判断顺序表中是否有元素,如果没有就没必要删了。
// 尾删 void SeqListPopBack(SeqList* ps) { assert(ps); assert(ps->size); ps->size--; }
尾插
尾插只要在size的位置放一个数据就可以了,也是比较简单的。
但是在插入之前我们为了防止容量不够,我们再分装一个函数来判断顺序表是否满了,如果满了我们就扩容。我们这里假设一次扩2倍,这个比较灵活,自己掌握就行。
//扩容 void SLCheckCapacity(SeqList* ps) { if (ps->size == ps->capacity) { SLDateType* cur = (SLDateType*)realloc(ps->arr, ps->capacity * 2 * sizeof(SLDateType)); if (cur == NULL) { perror("realloc"); return; } ps->arr = cur; ps->capacity *= 2; } }
//尾插 void SeqListPushBack(SeqList* ps, SLDateType x) { assert(ps); SLCheckCapacity(ps); ps->arr[ps->size] = x; ps->size++; }
头删
我们只需要将后面的数据依次往前挪动,然后将size–就可以了,这里从前往后挪,从后往前挪数据会被覆盖。
// 头插 void SeqListPushFront(SeqList* ps, SLDateType x) { assert(ps); SLCheckCapacity(ps); int end = ps->size; while (end > 0) { ps->arr[end] = ps->arr[end - 1]; end--; } //循环结束end==0 ps->arr[end] = x; ps->size++; }
查找
我们遍历顺序表,找到返回下标,找不到返回-1.
// 查找 找到返回下标否则返回-1 int SeqListFind(SeqList* ps, SLDateType x) { assert(ps); for (int i = 0; i < ps->size; i++) { if (ps->arr[i] == x) { return i; } } return -1; }
在下标pos的位置插入数据
我们只需要将pos位置到最后的数据依次往后挪动,然后将arr[pos]改为x,size++即可。但是我们依然要判断是否满容。
// 在pos下标位置插入x void SeqListInsert(SeqList* ps, int pos, SLDateType x) { assert(ps); //判断pos是否合法 assert(pos >= 0 && pos <= ps->size); SLCheckCapacity(ps); int end = ps->size; while (end > pos) { ps->arr[end] = ps->arr[end - 1]; end--; } ps->arr[pos] = x; ps->size++; }
删除pos位置的数据
这个就比较简单了,我们只需要将pos后面的数据依次往前挪,覆盖pos,size–,即可。
// 删除pos下标的数据 void SeqListErase(SeqList* ps, int pos) { assert(ps); //判断pos是否合法 assert(pos >= 0 && pos < ps->size); int begin = pos + 1; while (begin < ps->size) { ps->arr[begin - 1] = ps->arr[begin]; begin++; } ps->size--; }
修改pos位置的元素为x
这个直接将arr[pos]改为x就行啦,这里就不给大家实现了。
今天的分享就到这里,感谢大家的关注和支持。