1、线性表
线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使
用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串...
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,
线性表在物理上存储时,通常以数组和链式结构的形式存储 。
2、顺序表
2.1 顺序表的概念
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存
储。在数组上完成数据的增删查改。
顺序表一般可以分为:
1.静态顺序表:使用定长数组存储元素。
#define N 10 struct SeqList { int a[N]; int size; };
静态顺序表缺点:开的大了浪费空间,小了不够用。
优点:可以通过下标访问任意一个数组元素。
2.动态顺序表:使用动态开辟的数组存储。
动态的顺序表比起静态的顺序表就很灵活了,空间不够用可以随时增容。
2.2 接口
typedef int SLDateType; typedef struct SeqList { SLDateType* a; 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);
3、接口实现
3.1 初始化
void SeqListInit(SeqList* ps) { ps->a = (SLDateType*)malloc(sizeof(SLDateType) * 4); if (ps->a == NULL) { perror("malloc fail"); return; } ps->size = 0; ps->capacity = 4; }
使用malloc在堆区开辟动态内存,开4个整形的空间。容量也就初始化为4。
3.2 销毁
void SeqListDestroy(SeqList* ps) { assert(ps); free(ps); ps->a = NULL; ps->capacity = 0; ps->size = 0; }
在销毁的时候我们使用 free ,来将开辟的整个顺序表的内存释放掉,并将头指针置为空。
3.3 容量检测
void SeqCheckCapacity(SeqList* ps) { if (ps->capacity == ps->size) { SLDateType* tmp = (SLDateType*)realloc(sizeof(SLDateType), 2 * ps->capacity); ps->a = tmp; if (ps->a == NULL) { perror("realloc fail:"); return; } ps->capacity *= 2; } }
如果 4 个整型空间被使用完了就得继续开辟空间,因此我们使用 realloc 再开辟空间,合理的是再开辟的空间大小为原来的二倍。
3.4 打印数据
void SeqListPrint(SeqList* ps) { int i = 0; for (i = 0; i < ps->size; i++) { printf("%d ", ps->a[i]); } printf("\n"); }
对数组遍历,打印每个元素。
3.5 顺序表的头插
void SeqListPushFront(SeqList* ps, SLDateType x) { assert(ps); SeqCheckCapacity(ps); int begin = 0; for (begin = ps->size; begin > 0; begin--) { ps->a[begin] = ps->a[begin - 1]; } ps->a[0] = x; ps->size++; }
插入前我们调用容量检测函数对容量进行检测,函数内部已经写好了增容的程序。
分析:
在头插的时候我们要将数组从后往前移动。
3.6 顺序表的尾插
void SeqListPushBack(SeqList* ps, SLDateType x) { assert(ps); SeqCheckCapacity(ps); ps->a[ps->size] = x; ps->size++; }
顺序表的尾插还是先要检测容量是否足够。
分析:
3.7 顺序表的头删、尾删
//头删 void SeqListPopFront(SeqList* ps) { assert(ps); int begin = 0; for (begin = 0; begin < ps->size - 1; begin++) { ps->a[begin] = ps->a[begin + 1]; } ps->size--; } //尾删 void SeqListPopBack(SeqList* ps) { assert(ps); ps->a[ps->size - 1] = 0; ps->size--; }
分析:
头删
尾删
将 size-1 位置上的数字置 0,让 size-- 就完成尾删操作。
3.8 顺序表查找
约定:找到返回下标,没找到返回 -1。
int SeqListFind(SeqList* ps, SLDateType x) { int begin = 0; for (begin = 0; begin < ps->size - 1; begin++) { if (ps->a[begin] == x) return begin; } return -1; }
遍历数组就可以完成操作。
3.9 指定位置插入数据
void SeqListInsert(SeqList* ps, int pos, SLDateType x) { assert(pos>=0 && pos <= ps->size); int end = 0; for (end = ps->size - 1; end >= pos; end--) { ps->a[end + 1] = ps->a[end]; } ps->a[pos] = x; ps->size++; }
注意:
1.pos 位置要合法,0<= pos <= size;
2.依旧是从后往前覆盖;
3.如果pos = size 就是尾插,pos = 0 就是头插。
注意:
1.pos 位置要合法,0<= pos <= size-1;
2.从 pos 位置开始,到 size-1 位置结束,将 begin+1 的元素移到 begin 位置;
3.如果pos = size-1 就是尾删,pos = 0 就是头删。
完整代码在代码仓库,入口--->C语言: C语言: C语言的初级阶段时期 - Gitee.com
********************************************本片结束**********************************************
大牛的你看到有什么问题请一定要指出来。如果看了有收获,点赞收藏+关注三连一波。