顺序表的另外一种思考(拓展)
我打算跟着https://www.bilibili.com/video/BV1Cq4y1A7wo?p=3这个来加深自己对数据结构的理解,现在后面的数据结构方面的博客一般来说的话都来自于这个视频中的内容
首先先综述一下数据结构这个学科:数据结构就是定义一种性质并维护一种性质。所以说数据结构就是为结构定义加上结构操作
所以下面的内容都是先是结构定义然后是相应的操作与维护,再加上up主作为大佬的一些经验与思考。那么现在就开始数据结构的学习:
其实一开始学线性表的时候我一直又一种疑问就是这个线性表与数组有什么区别呢??其实区别就是在于是否对这个数据结构进行封装。封装之后这个线性表的功能就会强大很多。具体等等在后面的代码演示中会来描述:
#include <stdio.h> #include<stdlib.h> #include <time.h> typedef struct Vector { int* data; int size, length; } Vector;//这个就是一个结构定义 //第一个操作就是初始化 Vector* init(int n) { //首先要知道函数前面加上*说明函数的返回值是一个指针 Vector* vec = (Vector*)malloc(sizeof(Vector)); vec->data = (int*)malloc(sizeof(int) * n);//这个空间是指针型的 vec->size = n; vec->length = 0; return vec;//这个返回值是一个指针 } int Insert(Vector* vec, int ind, int val) { //首先进行合法性的判断 if (vec == NULL) return 0 ; if (vec->length == vec->size) return; if (ind<0 || ind>vec->length) { return 0; } //后面可以开始插入的操作了 for (int i = vec->length; i >= ind; i++) { vec->data[i] = vec->data[i - 1]; } vec->data[ind] = val; vec->length++; return 1; } //后面就是删除操作了 int erase(Vector* vec, int ind) { //首先还是判断插入的合理性 if (vec == NULL) return 0; if (vec->length == 0) return 0; if (ind < 0 || ind >= vec->length) return 0; for (int i = ind + 1; i < vec->length; i++) { vec->data[i - 1] = vec->data[i]; } vec->length--; return 1; } //记住有删除就有销毁 void clear(Vector* vec) { if (vec == NULL) { return; } //首先判空然后删除的时候先删除顺序表的数据域 free(vec->data); free(vec); return; } void output(Vector* vec) { printf("array(%d) = [", vec->length); for (int i = 0; i < vec->length; i++) { if (i != 0) printf(", "); printf("%d", vec->data[i]); } printf("]\n"); return; } int main() { #define MAX_OP 20; int op, ind, val; srand(time(0)); Vector* vec = init(MAX_OP); //如果要用到随机数的话就要初始化 for (int i = 0; i < MAX_OP; i++) { op = rand() % 2; ind = rand() % (vec->length + 1); val = rand() % 100; switch (op) { case 0: { printf("insert %d at %d to vector\n", val, ind); Insert(vec, ind, val); }break; case 1: { printf("erase item at %d from vector\n", ind); erase(vec, ind); }break; } } return 0; }
可以注意一下首先就是这个随机数是怎么搞的,怎么进行随机的操作处理测试,这个在后面的一些实验报告里面可以用到,很明显逼格很高。然后一些需要注意的点都在代码里面了。但是这个封装的用处没有体现出来啊,封装的目的不就是增加灵活性嘛!首先一个可以改进的点就是插入操作,如果空间不够用了怎么办呢?这样的话就不能插入了吗?事实上是可以的,这里可以采用realloc这个函数来进行。先不急,这里说一个修改概率的大小,我这里是要把这个操作修改为有百分之75的可能进行插入操作,有百分之25的可能进行删除操作。
int main() { #define MAX_OP 20; int op, ind, val; srand(time(0)); Vector* vec = init(MAX_OP); //如果要用到随机数的话就要初始化 for (int i = 0; i < MAX_OP; i++) { op = rand() % 4; ind = rand() % (vec->length + 1); val = rand() % 100; switch (op) { case 2: case 3: case 0: { printf("insert %d at %d to vector\n", val, ind); Insert(vec, ind, val); }break; case 1: { printf("erase item at %d from vector\n", ind); erase(vec, ind); }break; } }
return 0; }
下面是第一版的realloc函数,这个realloc函数就是把原来的数组进行了扩容。但是如果realloc函数在这个数组的后面没办法申请成功这段空间的话,realloc就会调用malloc,重新申请一块空间,并且把原来空间的内容复制过来。然后返回这个空间的首地址。但是原来这个空间没有free,所以这个函数还会把原来的空间释放掉。如果申请空间失败了,这realloc就会返回一个空地址,但是原来的空间没动。如果失败了原来的空间地址就丢了,就造成了内存泄漏!
现在要修改这个bug
int expand(Vector *vec) { //这个扩容就是用realloc来进行的 int new_size = vec->size * 2; int* p = (int*)realloc(vec->data, sizeof(int) * new_size); if (p == NULL) return 0; vec->size = new_size; vec->data = p; return 1; }
这样就避免的内存泄漏的问题了!