zoukankan      html  css  js  c++  java
  • C语言利用动态数组实现顺序表(不限数据类型)

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

    顺序表结构体

      实现顺序表结构体的三个要素:(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 }

    遍历操作

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

     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
    ", 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
    ", *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
    ", capaArray(arr));
    54     Insert(arr, 100, &p5);
    55     printf("%d
    ", capaArray(arr));
    56     Insert(arr, 1, &p6);
    57     Foreach(arr, print);
    58     printf("-----------------
    ");
    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 }
  • 相关阅读:
    用c#写一个json的万能解析器
    使用Xpath从网页中获取数据
    pdf文件流生成pdf文件
    AES加密和Base64混合加密
    参数请求post, get , delete中的基本使用(2)
    参数请求post, get , delete中的基本使用(1)
    java多线程打印ABC
    我的开发工具
    MyBatis的#与$
    struts1
  • 原文地址:https://www.cnblogs.com/qinguoyi/p/10241670.html
Copyright © 2011-2022 走看看