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

顺序表结构体

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

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

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

初始化

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

 //初始化数组,初始化结构体和里面的元素。初始化之后返回该数组,写为void*
void *Init(int capacity_){
if (capacity_ <= ){
return NULL;
} struct DynamicArray *arr = malloc(sizeof(struct DynamicArray));//开辟一个结构体就可以了
if (NULL == arr){
return NULL;
} arr->capacity = capacity_;
arr->addr = malloc(sizeof(void*)*arr->capacity);//对数组开辟内存
arr->size = ; return arr;
}

插入操作

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

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

 //插入值,从后往前挨个移动一位,如果插入的值过大,则扩大数组
void Insert(void *arr_, int pos, void *data){ struct DynamicArray *arr = (struct DynamicArray *)arr_; if (NULL == arr || NULL == data){
return;
} //对于无效的pos,默认插入到现有元素的后面一个
//if (pos < 0 || pos > arr->capacity)
if (pos < || pos > arr->size)
{
pos = arr->size;
} //每次调用插入函数都会插入值,因此arr->size++,size一直增长,且没有限制
if (arr->size >= arr->capacity)
//if (arr->size > arr->capacity)
{ //开辟新内存
int newcapacity = arr->capacity * ;
void **newaddr = malloc(sizeof(void *)*newcapacity); //拷贝数据,尽量使用内存拷贝函数
memcpy(newaddr, arr->addr, sizeof(void *) * arr->capacity); //释放原空间
if (arr->addr != NULL){
free(arr->addr);
arr->addr = NULL;
} //修改指针指向
arr->addr = newaddr;
arr->capacity = newcapacity;
} for (int i = arr->size - ; i >= pos; --i){
arr->addr[i + ] = arr->addr[i];
}
arr->addr[pos] = data; //添加过后,size变化
arr->size++;
}

遍历操作

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

 //遍历
void Foreach(void *arr_, void(*_callback)(void *)){
struct DynamicArray * arr = (struct DynamicArray *)arr_;
if (NULL == arr || NULL == _callback){
return;
}
for (int i = ; i < arr->size; ++i){
_callback(arr->addr[i]);
}
}

删除操作

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

 //删除(按值删除,按位置删除)
void DeletePos(void *arr_, int pos){
struct DynamicArray *arr = arr_;
if (NULL == arr){
return;
} for (int i = pos; i < arr->size - ; ++i){
arr->addr[i] = arr->addr[i + ];
} arr->size--;//size会减小
}
void DeleteValue(void *arr_, void * data, int(*_compare)(void *, void*)){
struct DynamicArray *arr = arr_;
if (NULL == arr || NULL == data || NULL == _compare){
return;
}
for (int i = ; i < arr->size; i++){
if (_compare(arr->addr[i], data)){
DeletePos(arr, i);
break;
}
}
//arr->size--; DeletePos里面已经有size--了
}

销毁操作

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

 //销毁
void Destory(void * arr_){
struct DynamicArray *arr = arr_;
if (NULL == arr){
return;
}
if (arr->addr != NULL){
free(arr->addr);
arr->addr = NULL;
}
if (arr != NULL){
free(arr);
arr = NULL;
}
}

元素个数和数组大小函数

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

 int capaArray(void *arr_){
struct DynamicArray *arr = (struct DynamicArray *)arr_;
return arr->capacity;
} int sizeArray(void *arr_){
struct DynamicArray *arr = (struct DynamicArray *)arr_;
return arr->size;
}

测试

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

 struct Person{
char name[];
int age;
}; // 自定义输出函数
void print(void *data_){
if (NULL == data_){
return;
}
struct Person *data = (struct Person *)data_;
printf("name:%s, age:%d\n", data->name, data->age);
}
//整型自定义输出函数,访问时需要解引用
void printInt(void *data_){
if (NULL == data_){
return;
}
int *data = (int *)data_;
printf("age:%d\n", *data);
} //自定义比较函数
int compare(void *d1, void *d2){
if (NULL == d1|| NULL == d2){
return ;
}
struct Person *p1 = d1;
struct Person *p2 = d2; return (strcmp(p1->name, p2->name) == && (p1->age == p2->age));
} void test(){
struct Person p1 = {"aaa", };
struct Person p2 = { "bbb", };
struct Person p3 = { "ccc", };
struct Person p4 = { "ddd", };
struct Person p5 = { "eee", };
struct Person p6 = { "fff", };
//int p1 = 1;
//int p2 = 2;
//int p3 = 3;
//int p4 = 4;
//int p5 = 5; void * arr = Init();
Insert(arr, , &p1);
Insert(arr, , &p2);
Insert(arr, , &p3);
Insert(arr, , &p4);
printf("%d\n", capaArray(arr));
Insert(arr, , &p5);
printf("%d\n", capaArray(arr));
Insert(arr, , &p6);
Foreach(arr, print);
printf("-----------------\n");
DeleteValue(arr, &p2, compare);
Foreach(arr, print);
Destory(arr); } int main(){ test();
system("pause");
return ;
}

C语言利用动态数组实现顺序表(不限数据类型)的更多相关文章

  1. C++利用动态数组实现顺序表(不限数据类型)

    通过类模板实现顺序表时,若进行比较和遍历操作,模板元素可以通过STL中的equal_to仿函数实现,或者通过回调函数实现.若进行复制操作,可以采用STL的算法函数,也可以通过操作地址实现.关于回调函数 ...

  2. "《算法导论》之‘线性表’":基于动态分配的数组的顺序表

    我们利用静态分配的数组来实现的顺序表的局限还是挺大的,主要在于它的容量是预先定好的,用户不能根据自己的需要来改变.如果为了后续用户能够自己调整顺序表的大小,动态地分配数组空间还是很有必要的.基于动态分 ...

  3. "《算法导论》之‘线性表’":基于静态分配的数组的顺序表

    首先,我们来搞明白几个概念吧(参考自网站数据结构及百度百科). 线性表 线性表是最基本.最简单.也是最常用的一种数据结构.线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外, ...

  4. c语言,动态数组

    试着直接malloc一个2*3*4的空间来模拟数组: #include <stdio.h> #include <malloc.h> int main(void) { int** ...

  5. 使用JAVA数组实现顺序表

    1,引入了JAVA泛型类,因此定义了一个Object[] 类型的数组,从而可以保存各种不同类型的对象. 2,默认构造方法创建了一个默认大小为16的Object数组:带参数的构造方法创建一个指定长度的O ...

  6. c语言中动态数组的建立

    一维动态数组的创建,这个比较简单,直接上代码 #define _CRT_SECURE_NO_DEPRECATE #include<stdio.h> #include<stdlib.h ...

  7. HDU 2819 ——Swap——————【最大匹配、利用linker数组、邻接表方式】

     Swap Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Status ...

  8. c语言构建动态数组

    #include <stdio.h> #include <stdlib.h> int main(void) { int len; int * arr; printf(" ...

  9. C语言学习-数据结构 - 倒插法顺序表

    // test20161106.cpp : Defines the entry point for the console application. // #include "stdafx. ...

随机推荐

  1. UVA 12676 Inverting Huffman

    题目链接:https://vjudge.net/problem/UVA-12676 题目大意 一串文本中包含 N 个不同字母,经过哈夫曼编码后,得到这 N 个字母的相应编码长度,求文本的最短可能长度. ...

  2. 20140319 const sizeof define 编译时分配内存

    1.面试宝典预处理,const,sizeof Define作用定义函数: //用一个宏定义FIND求一个结构体struc里某个变量相对于struc的偏移量,如FIND(student,a)//等于0 ...

  3. solr 查询同一个core 的关联字段

    实现一个core里面多个字段的关联查询: 应用场景是: 词, 句子,文章 希望通过查询实现词,句子,文章里面共同有的关键字 private static CloudSolrServer cloudSo ...

  4. Java设计模式(一)外观模式(门面模式)- 结构型模式

    模式的定义 门面模式(Facade Pattern)也叫做外观模式,是一种比较常用的封装模式,其定义如下:要求一个子系统的外部与其内部通信必须通过一个统一的对象进行.门面模式提供一个高层次的接口,使得 ...

  5. Neo4j中實現自定義中文全文索引

    資料庫檢索效率時,一般首要優化途徑是從索引入手,然後根據需求再考慮更復雜的負載均衡.讀寫分離和分散式水平/垂直分庫/表等手段:索引通過資訊冗餘來提高檢索效率,其以空間換時間並會降低資料寫入的效率,因此 ...

  6. linux mysql主从复制配置

    1.设置主库master的servie-id值并且开启bin-log功能参数vi /etc/my.cnf修改my.cnf的参数:[mysqld]server-id=1 //每一个库的server-id ...

  7. cms系统视频分享

    cms_001-CMS系统功能需求简介-1.avicms_002-如何采用用例分析方法来理解需求-1.avicms_003-后台管理系统用例-1.avicms_004-实现验证码的初步思路-1.avi ...

  8. ie8以下不兼容h5新标签的解决方法

    HTML5新添了一些语义化标签,他们能让代码语义化更直观易懂,有利于SEO优化.但是此HTML5新标签在IE6/IE7/IE8上并不能识别,需要进行JavaScript处理. 解决思路就是用js创建h ...

  9. 理解Spring框架中Bean的5个作用域

    当通过spring容器创建一个Bean实例时,不仅可以完成Bean实例的实例化,还可以为Bean指定特定的作用域.Spring支持如下5种作用域: singleton:单例模式,在整个Spring I ...

  10. LINUX centos 7.2/7.3 搭建LAMP环境

    首先我们先查看下centos的版本信息 #适用于所有的linux lsb_release -a #或者 cat /etc/redhat-release #又或者 rpm -q centos-relea ...