您好,登录后才能下订单哦!
这篇文章主要讲解了“C语言顺序表如何实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C语言顺序表如何实现”吧!
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组 上完成数据的增删查改。
顺序表一般可以分为:
静态顺序表:使用定长数组存储元素,元素的数目无法进行修改。
//顺序表的静态存储 #define N 7 typedef int SLDataType; typedef struct SeqList { SLDataType array[N];//定长数组 size_t size;//有效数据的个数 }SeqList;
动态顺序表
//顺序表的动态存储 typedef struct SeqList { SLDataType* array;//指向动态开辟的数组,空间不够可以增容 size_t size;//有效数据的个数 size_t capacity;//容量空间的大小 };
静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空间开多了浪 费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小,所以下面我们实现动态顺序表。
typedef int SLDataType;//顺序表中存储的数据,此处假设是int型 typedef struct SeqList { int* a;//指向动态开辟的数组空间,空间可以随时增容 int size;//存储数据个数 int capacity;//存储空间大小 }SL,SeqList;
void SeqListInit(SeqList* psl);//声明 void SeqListInit(SeqList* psl) { assert(psl);//进行断言是因为当psl为NULL时,下面的操作将无法进行,因为空指针是无法进行解引用的。 psl->a = NULL; psl->size = 0; psl->capacity = 0; }//函数实现
注意:进行断言是因为当psl为NULL时,下面的操作将无法进行,因为空指针是无法进行解引用的,后面也是如此。
void SeqListDestroy(SeqList* psl); void SeqListDestroy(SeqList* psl) { assert(psl); free(psl->a); psl->a = NULL; psl->capacity = psl->size = 0; }
注意:free后面括号中的指针必须是malloc开辟出来的那块空间,且不能有任何的偏差(即指针不能发生移动)。
下面进行举例:
像上面这样使用是完全没有问题的,但是像下面这样进行使用就出现了问题:
tmp进行自增操作后,就指向了下图所示位置:
free的位置是tmp++后的位置,这不符合C语言的规定,且即使正常的释放掉了,前面的那一块int空间也将引起内存泄漏问题,即动态开辟的内存忘记释放。
void SeqListPushBack(SeqList* psl,SLDataType x);//声明 void SeqListPushBack(SeqList* psl, SLDataType x) { assert(psl); //如果满了,就进行扩容 SeqListCheckCapacity(psl); psl->a[psl->size] = x; psl->size++; }
void SeqListPopBack(SeqList* psl); void SeqListPopBack(SeqList* psl) { assert(psl); if(psl->size > 0) { psl->size -= 1; } }
void SeqListPushFront(SeqList* psl, SLDataType x); void SeqListPushFront(SeqList* psl, SLDataType x) { assert(psl); SeqListCheckCapacity(psl); int end = psl->size - 1; while (end >= 0) { psl->a[end+1] = psl->a[end]; --end; } psl->a[0] = x; psl->size++; }
顺序表的头插会涉及到后续元素的移动,头插时要将顺序表中的元素从后面开始进行移动,因为从前面开始移动的话会出现覆盖现象。下面是图示:
同理,如果想要元素不被覆盖,就只能从前向后进行移动。
void SeqListPopFront(SeqList* psl); void SeqListPopFront(SeqList* psl) { //挪出数据,腾出头部空间 //方法一:从1开始移动 /*assert(psl); if (psl->size > 0) { int begin = 1; while (begin < psl->size) { psl->a[begin - 1] = psl->a[begin]; begin++; } psl->size--; }*/ //方法二:从0开始移动 assert(psl); if (psl->size > 0) { int begin = 0; while (begin < psl->size - 1) { psl->a[begin] = psl->a[begin + 1]; begin++; } psl->size--; } }
下图是两种移动方式的区别:
问:为什么不可以直接将指针进行加1操作?
free释放空间时的指针和malloc开辟空间的指针必须相同
mallo开辟的空间存在浪费,即0的那块空间被浪费,无法进行使用,因为那块空间是被合法申请的。
void SeqListCheckCapacity(SeqList* psl); void SeqListCheckCapacity(SeqList* psl) { assert(psl); if (psl->capacity == psl->size) { size_t newCapacity = psl->capacity == 0 ? 4 : psl->capacity * 2; SLDataType* tmp = (SLDataType*)realloc(psl->a, sizeof(SLDataType) * newCapacity); if (tmp == NULL) { printf("realloc fail\n"); exit(-1); } else { psl->a = tmp; psl->capacity *= 2; } } }
注意点1:此处考虑使用的是如果容量不够,就将容量扩容为原容量的两倍,但是最开始的容量是0,所以要考虑到最开始为0的情况。
注意点2:要对扩容是否成功进行检测,即判断刚申请的空间是否为空。
void SeqListInsert(SeqList* psl,size_t pos,SLDataType x); void SeqListInsert(SeqList* psl, size_t pos, SLDataType x) { assert(psl); //较为温和的检查方式 /*if (pos > psl->size) { exit(-1); }*/ assert(pos <= psl->size);//较为暴力的检查方式 SeqListCheckCapacity(psl); size_t end = psl->size; while (end > pos) { psl->a[end] = psl->a[end-1]; --end; } psl->a[pos] = x; psl->size++; }
注意:
问:为什么end从psl->size开始?
答:此处需要注意的是pos和end的类型,为什么呢?因为两者都是无符号类型,所以尤其注意当end到了-1的时候,就会变成一个很大的数字,此时如果以此作为下标进行访问,一定会出现越界访问内存的情况,考虑一种极端情况,当pos为0的时候,end最小的时候就是为0,所以此时不会出现越界的情况,但是如果end是从psl->size - 1开始的话,那么while循环体内的语句就变成下面这样:
while(end > pos) { psl->a[end+1] = psl->a[end]; --end; }
最后end的最小值会变成-1,但是因为end是size_t类型,所以会变成一个很大的数字,在whle()循环条件判定时条件始终是满足的,同时在进入循环体内之后,会出现越界访问内存的操作。
void SeqListErase(SeqList* psl, size_t pos); void SeqListErase(SeqList* psl, size_t pos) { assert(psl); assert(pos <= psl->size); size_t begin = pos+1; while (begin < psl->size) { psl->a[begin-1] = psl->a[begin]; ++begin; } psl->size--; }
void SeqListPrint(SeqList* psl); void SeqListPrint(SeqList* psl) { assert(psl); for (int i = 0; i < psl->size; i++) { printf("%d ", psl->a[i]); } printf("\n"); }
int SeqListFind(SeqList* psl,SLDataType x); int SeqListFind(SeqList* psl, SLDataType x) { assert(psl); for (int i = 0; i < psl->size; i++) { if (psl->a[i] == x) return i;//找到了对应元素,返回相应的下标 } return -1;//说明没有找到对应的元素 }
void SeqListModify(SeqList* psl, size_t pos, SLDataType x); void SeqListModify(SeqList* psl, size_t pos, SLDataType x) { assert(psl); assert(pos < psl->size); psl->a[pos] = x; }
感谢各位的阅读,以上就是“C语言顺序表如何实现”的内容了,经过本文的学习后,相信大家对C语言顺序表如何实现这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。