博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C语言利用动态数组实现顺序表(不限数据类型)
阅读量:6089 次
发布时间:2019-06-20

本文共 5634 字,大约阅读时间需要 18 分钟。

实现任意数据类型的顺序表的初始化,插入,删除(按值删除;按位置删除),销毁功能。、

顺序表结构体

  实现顺序表结构体的三个要素:(1)数组首地址;(2)数组的大小;(3)当前数组元素的个数。

1 //顺序表结构体2 struct DynamicArray{3     void **addr; //指向数组的首地址(指向数组的指针)4     int capacity; //数组的大小5     int size; //当前数组元素的个数6 };

  注意事项:void **addr为二级指针,即数组的元素也为指针,因为我们并不知道用户的输入数据是什么类型,操作数据的地址是最安全的方法。

初始化

  对顺序表进行初始化,实际上为初始化顺序表内的各个成员,另外对输入的参数做边界处理。

1 //初始化数组,初始化结构体和里面的元素。初始化之后返回该数组,写为void* 2 void *Init(int capacity_){ 3     if (capacity_ <= 0){ 4         return NULL; 5     } 6      7     struct DynamicArray *arr = malloc(sizeof(struct DynamicArray));//开辟一个结构体就可以了 8     if (NULL == arr){ 9         return NULL;10     }11 12     arr->capacity = capacity_;13     arr->addr = malloc(sizeof(void*)*arr->capacity);//对数组开辟内存14     arr->size = 0;15 16     return arr;17 }

插入操作

  对于顺序表的插入操作,需要在pos位置处开始的元素统一往后移动一个位置,处理方式为:从后往前挨个移动,从前往后会覆盖。

  注意:(1)考虑顺序表的顺序插入和乱序插入,12行的代码;(2)若顺序表被填满,则需要对现有数组进行扩大空间,这里涉及到四步操作:开辟内存、拷贝数据(尽量使用memcpy)、释放原内存、修改指针指向。(3)对输入参数做边界处理。

1 //插入值,从后往前挨个移动一位,如果插入的值过大,则扩大数组 2 void Insert(void *arr_, int pos, void *data){ 3  4     struct DynamicArray *arr = (struct DynamicArray *)arr_; 5  6     if (NULL == arr || NULL == data){ 7         return; 8     } 9 10     //对于无效的pos,默认插入到现有元素的后面一个11     //if (pos < 0 || pos > arr->capacity)12     if (pos < 0 || pos > arr->size)13     {14         pos = arr->size;15     }16 17     //每次调用插入函数都会插入值,因此arr->size++,size一直增长,且没有限制18     if (arr->size >= arr->capacity)19     //if (arr->size > arr->capacity)20     {21 22         //开辟新内存23         int newcapacity = arr->capacity * 2;24         void **newaddr = malloc(sizeof(void *)*newcapacity);25 26         //拷贝数据,尽量使用内存拷贝函数27         memcpy(newaddr, arr->addr, sizeof(void *) * arr->capacity);28 29         //释放原空间30         if (arr->addr != NULL){31             free(arr->addr);32             arr->addr = NULL;33         }34 35         //修改指针指向36         arr->addr = newaddr;37         arr->capacity = newcapacity;38     }39 40     for (int i = arr->size - 1; i >= pos; --i){41         arr->addr[i + 1] = arr->addr[i];42     }43     arr->addr[pos] = data; //添加过后,size变化44     arr->size++;45 }

遍历操作

  遍历一般的作用为打印数据,但这里并不知道用户的是什么数据,这里由回调函数进行打印()。

1 //遍历 2 void Foreach(void *arr_, void(*_callback)(void *)){ 3     struct DynamicArray * arr = (struct DynamicArray *)arr_; 4     if (NULL == arr || NULL == _callback){ 5         return; 6     } 7     for (int i = 0; i < arr->size; ++i){ 8         _callback(arr->addr[i]); 9     }10 }

删除操作

  这里分为按值删除和按位置删除,其中按值操作调用了按位置操作的代码,因此需要注意size--的问题。另外,按值操作也使用了回调函数。

1 //删除(按值删除,按位置删除) 2 void DeletePos(void *arr_, int pos){ 3     struct DynamicArray *arr = arr_; 4     if (NULL == arr){ 5         return; 6     } 7  8     for (int i = pos; i < arr->size - 1; ++i){ 9         arr->addr[i] = arr->addr[i + 1];10     }11 12     arr->size--;//size会减小13 }14 void DeleteValue(void *arr_, void * data, int(*_compare)(void *, void*)){15     struct DynamicArray *arr = arr_;16     if (NULL == arr || NULL == data || NULL == _compare){17         return;18     }19     for (int i = 0; i < arr->size; i++){20         if (_compare(arr->addr[i], data)){21             DeletePos(arr, i);22             break; 23         }24     }25     //arr->size--; DeletePos里面已经有size--了26 }

销毁操作

  注意事项:需要先释放成员函数,再释放结构体。

1 //销毁 2 void Destory(void * arr_){ 3     struct DynamicArray *arr = arr_; 4     if (NULL == arr){ 5         return; 6     } 7     if (arr->addr != NULL){ 8         free(arr->addr); 9         arr->addr = NULL;10     }11     if (arr != NULL){12         free(arr);13         arr = NULL;14     }15 }

元素个数和数组大小函数

  之所以提供这两个函数,是因为不希望用户能直接看到我们定义的结构体内部,也不希望用户可以随便更改,因此我们各个函数的返回值都是void类型,这里提供两个函数,以便用户可以查看元素的个数和数组的大小。

1 int capaArray(void *arr_){2     struct DynamicArray *arr = (struct DynamicArray *)arr_;3     return arr->capacity;4 }5 6 int sizeArray(void *arr_){7     struct DynamicArray *arr = (struct DynamicArray *)arr_;8     return arr->size;9 }

测试

  进行测试时,需要自定义打印和比较回调函数,不需要关心void*data是什么,仅仅实现自定义数据的比较和打印即可。另外回调函数使用时不需要任何参数,只需要函数名;另外,测试了结构体和整型顺序表,可以对顺序表结构体中的void **addr为二级指针有更好的理解。

1 struct Person{ 2     char name[100]; 3     int age; 4 }; 5  6 // 自定义输出函数 7 void print(void *data_){ 8     if (NULL == data_){ 9         return;10     }11     struct Person *data = (struct Person *)data_;12     printf("name:%s, age:%d\n", data->name, data->age);13 }14  //整型自定义输出函数,访问时需要解引用15 void printInt(void *data_){16     if (NULL == data_){17         return;18     }19     int *data = (int *)data_;20     printf("age:%d\n", *data);21 }22 23 //自定义比较函数24 int compare(void *d1, void *d2){25     if (NULL == d1|| NULL == d2){26         return 0;27     }28     struct Person *p1 = d1;29     struct Person *p2 = d2;30 31     return (strcmp(p1->name, p2->name) == 0 && (p1->age == p2->age));32 }33 34 void test(){35     struct Person p1 = {
"aaa", 10};36 struct Person p2 = { "bbb", 20 };37 struct Person p3 = { "ccc", 30 };38 struct Person p4 = { "ddd", 40 };39 struct Person p5 = { "eee", 50 };40 struct Person p6 = { "fff", 60 };41 //int p1 = 1;42 //int p2 = 2;43 //int p3 = 3;44 //int p4 = 4;45 //int p5 = 5;46 47 48 void * arr = Init(4);49 Insert(arr, 1, &p1);50 Insert(arr, 2, &p2);51 Insert(arr, 3, &p3);52 Insert(arr, 4, &p4);53 printf("%d\n", capaArray(arr));54 Insert(arr, 100, &p5);55 printf("%d\n", capaArray(arr));56 Insert(arr, 1, &p6);57 Foreach(arr, print);58 printf("-----------------\n");59 DeleteValue(arr, &p2, compare);60 Foreach(arr, print);61 Destory(arr);62 63 }64 65 66 int main(){67 68 test();69 system("pause");70 return 0;71 }

 

转载于:https://www.cnblogs.com/qinguoyi/p/10241670.html

你可能感兴趣的文章
libev与libuv的区别
查看>>
iOS 为什么使用xcode8上传app包到appStore无法构建版本
查看>>
Tomcat优化步骤【转】
查看>>
CRC 自动判断大端 小端
查看>>
原来这样可以轻松恢复回收站删除文件
查看>>
DisparityCostVolumeEstimator.cpp
查看>>
(转)git中关于fetch的使用
查看>>
mongo DB for C#
查看>>
caffe整体框架的学习的博客,这个博客山寨了一个caffe框架
查看>>
git只拉取github部分代码的方法
查看>>
[LeetCode] Construct Quad Tree 建立四叉树
查看>>
如何避免SHRINKDATABASE & SHRINKFILE 产生索引碎片(转载)
查看>>
【SSH网上商城项目实战02】基本增删查改、Service和Action的抽取以及使用注解替换xml...
查看>>
高阶函数简述 js
查看>>
Java CompletableFuture:allOf等待所有异步线程任务结束
查看>>
Highmaps网页图表教程之图表配置项结构与商业授权
查看>>
mysql 5.6.33发布
查看>>
java 获取URL链接 内容
查看>>
Linux 命令详解(二)awk 命令
查看>>
Android动态载入Dex机制解析
查看>>