数据结构-C语言实现动态扩容数组

Posted LeonYi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构-C语言实现动态扩容数组相关的知识,希望对你有一定的参考价值。

// resizable array


/*
Think about a set of functions that provide a mechanism of resizable array of int. 
    • Growable 
    • Get the current size 
    • Access to the elements
*/

/*
the Interface
    • Array array_create(int init_size); 
    • void array_free(Array *a); 
    • int array_size(const Array *a); 
    • int* array_at(Array *a, int index); 
    • void array_inflate(Array *a, int more_size);
*/
typedef struct 
 
    int* array; 
    int size; 
 Array;

#define BLOCK_SIZE 16

Array array_create(int init_size) 
 
    Array a; 
    a.array = (int*)malloc(sizeof(int) * init_size); 
    a.size = init_size; 
    return a; 


void array_free(Array *a) 
 
    free(a->array); 
    a->size = 0; 


int array_size(const Array *a) 
 
    return a->size; 


void array_inflate(Array *a, int more_size) 
 
    int* p = (int*)malloc(sizeof(int) * (a->size + more_size)); 
    for ( int i=0; i<a->size; i++ ) 
    
        p[i] = a->array[i]; 
    
    free(a->array); 
    a->array = p;
    a->size = a->size + more_size; 


void array_inflate(Array *a, int more_size) 

    int* p = (int*)malloc(sizeof(int) * (a->size + more_size)); 
    memcpy((void*) p, (void*) a->array, a->size*sizeof(int)); 
    free(a->array); 
    a->array = p; a->size = a->size+more_size; 



int* array_at(Array *a, int index) 
 
    if ( index >= a->size ) 
     
        array_inflate(a, 
            (index/BLOCK_SIZE+1) * BLOCK_SIZE-a->size); 
     
    return &(a->array[index]); 



int test()

    // use array_at()
    Array a = array_create(10); 
    *(array_at(&a, 5)) = 6; 
    *(array_at(&a, 10)) = *(array_at(&a, 5));


/*
will it be better
    • to have two access functions: 
    • array_get(), and 
    • array_set()

Array a = array_create(10); 
array_set(&a, 5, 6); 
array_set(&a, 10, array_get(&a, 5));
*/

未完待续,后续补充链式分块扩容数组(二维数组),以及C++ Vector实现

参考:C语言进阶- 浙江大学Mooc

动态数组的实现(c语言)----数据结构


对于数据结构来说,最基本的就是动态数组了,那么动态数组到底该怎么来实现它呢,可能对于刚学习数据结构的人来说还是有点难理解的,下面的就具体来写一写动态数组的实现过程。
首先我们来想一下对于一个动态的数组,它要能实现什么功能呢? 动态数组相比静态数组最主要的功能那当然是可以动态开辟数组的长度了,当然还包括可以删除某个数组中的元素。下面我们来具体实现这些功能。

1.定义数组的结构体

对于一个动态开辟的数组,那么我们怎么能做到呢,这里我们首先想到可能就是用一个结构体来封装一些数组的属性, 从而来达到我们想要的操作。

首先结构体中首先肯定要先定义一个数组,那么数组我们应该定义为什么类型呢? 一般来说我们是不知道用户传过来的数据是什么类型的,所以为了可以接收任意类型的数据我们可以将数据类型定义为void*) ,**这样的话我们就可以接收任意类型的数据了,包括自定义类型的数据。**当然我们还需要知道数组中已有元素的个数,以及数组的容量,这样的话方便我们后续进行扩容操作。

struct dynamicArray
{
	void** parray;//数组中的数据类型定义为void* 
	int mSize; //数组实际大小
	int mCapacity;//数组容量
};

2.初始化数组

对于初始化数组来说,就是开辟出一个固定容量的数组,方便后续的操作。具体看下面代码

struct dynamicArray* initArray(int capacity)
{
	if (capacity <= 0)//如果最大容量小于等于0直接返回
	{
		return NULL;
	}
	//为结构体开辟一块空间
	struct dynamicArray* array = malloc(sizeof(struct dynamicArray)); 
	//开辟失败直接返回NULL
	if (array == NULL)
	{
		return NULL;
	}
	//初始化结构体中的一些变量
	array->mCapacity = capacity;
	array->mSize = 0;
	//给结构体中的数组开辟一块capacity的空间
	array->parray = malloc(sizeof(void*) * array->mCapacity);
	return array;
}

3.按位置插入元素

**对于元素的插入,我们需要注意的是数组的大小是否达到了它的容量,如果到达了它的容量,我们就需要进行扩容操作。**下面看代码具体实现过程。注释写的很清楚

void insertArray(struct dynamicArray* array, int pos, void* date)
{
	if (array == NULL)
	{
		return;
	}
	if (date == NULL)
	{
		return;
	}
	//判断位置的合理性
	if (pos<0 || pos>array->mSize)
	{
		pos = array->mSize;
	}
	//判断当前数组容量是否已经满了,满了的话,要进行扩容操作
	if (array->mSize == array->mCapacity)
	{
		//设置新的量为之前容量的两倍
		int newCapacity = array->mCapacity * 2;
		//按照新的容量开辟在堆区开辟一块新的空间
		void** newSpace = malloc(sizeof(void*) * newCapacity);
		//将之前数组中的元素拷贝到新的空间之中
		memcpy(newSpace, array->parray, sizeof(void*) * array->mCapacity);
		//将之前数组空间释放掉
		free(array->parray);
		//将数组指向这块新空间
		array->parray = newSpace;
		//将数组的容量也进行更新
		array->mCapacity = newCapacity;
	}
	//将pos位置后面的数据都往后一位
	for (int i = array->mSize - 1; i >= pos; i--)
	{
		array->parray[i + 1] = array->parray[i];
	}
	//将数据插入到pos位置
	array->parray[pos] = date;
	//对数组的大小进行更新
	array->mSize++;
}

4.删除元素

对于删除元素,我这里提供两种不同的方法来进行删除,一种是通过位置来进行删除,一种是通过数据类型来进行删除。

4.1删除某个位置的元素

在数组中删除pos位置元素,最简单的方式就是直接覆盖掉pos位置的元素,然后将数组中的元素的个数减一就好了。这个比较简单,直接看下面的代码。

//删除某个位置的元素
void deletDate(struct dynamicArray* arr, int pos)
{
	if (arr == NULL)
		return;
	if (pos<0 || pos>arr->mSize - 1)
	{
		return;
	}

	for (int i = pos; i < arr->mSize - 1; i++)
	{
		arr->parray[i] = arr->parray[i + 1];
	}
	arr->mSize--;
}

4.2通过数据来删除某个元素

通过数据来删除某个元素,比通过位置来删除某个数据要复杂一点,因为我们设定的数据类型为void* 的数据类型,我们也不知道用户会传什么样的数据类型给我们。所以我们就要想一种通用的数据比较方法。这里我们就可以用回调函数来实现这一功能,关于回调函数,可以去看一下下面这篇博客中的简单介绍 第6小结函数指针数组中有简单介绍回调函数
下面我设计了一个myCompar的函数指针,用来做回调函数,对于怎么用,在后面测试环节我会将到。

void removeDate(struct dynamicArray* arr, void* date, int(*myCompar)(void*, void*))
{
	if (arr == NULL)
		return;
	if (date == NULL)
		return;

	for (int i = 0; i < arr->mSize; i++)
	{
		if (myCompar(arr->parray[i], date))
		{
			deletDate(arr, i);
			break;
		}
	}

}

5.遍历数组

遍历数组,我们主要是想要看到数组中的内容,这里我们同样提供一个回调函数来遍历数组。

void foreachArray(struct dynamicArray* array, void(*myForeach)(void*))
{
	if (array == NULL)
	{
		return;
	}

	for (int i = 0; i < array->mSize; i++)
	{
		myForeach(array->parray[i]);
	}
}

6.销毁数组

销毁数组时,我们要注意我们在堆区开辟了多少几块空间,这里我们首先是为结构体开辟了一块空间,然后结构体中的数组我们又开辟了一块空间,所以我们进行释放的时候,应该先释放里面的数组,然后在释放外面的结构体。

void destoryArr(struct dynamicArray* array)
{
	if (array == NULL)
		return;
	if (array->parray != NULL) //先删除里面的
	{
		free(array->parray);
		array->parray = NULL;
	}

	free(array);
	array = NULL;
}

7.测试代码功能

这里我们选用一个自定义的数据类型来进行测试。然后根据数据类型完成比较数据和打印数据两个回调函数。

//定义一个人的数据类型
struct Person
{
	char name[50];
	int age;
};
//比较数据信息,用作回调函数
int mycampar(void* date1, void* date2)
{
	struct Person* p1 = date1;
	struct Person* p2 = date2;
	//p1,p2数据相同时返回真
	return (strcmp(p1->name, p2->name) == 0) && (p1->age == p2->age);

}
//打印信息,用作遍历数组的回调函数
void myPrint(void* date)
{
	struct Person* p = date;
	printf("姓名:%s  年龄: %d\\n", p->name, p->age);
}

void test01()
{
	//开辟一个容量为5 的数组
	struct dynamicArray* array= initArray(5);
	printf("数组扩容前容量: %d\\n", array->mCapacity);
	//创建6个数据
	struct Person p1 = { "陈",10 };
	struct Person p2 = { "卢",12 };
	struct Person p3 = { "张",14 };
	struct Person p4 = { "王",15 };
	struct Person p5 = { "周",11 };
	struct Person p6 = { "李",16 };
	//插入这6个数据看是否容量会扩增
	insertArray(array, 0, &p1);
	insertArray(array, 1, &p2);
	insertArray(array, 0, &p3);
	insertArray(array, 2, &p4);
	insertArray(array, 6, &p5);
	insertArray(array, 5, &p6);
	printf("数组扩容后容量: %d\\n", array->mCapacity);
	//调用回调函数打印数组内用
	foreachArray(array, myPrint);

	printf("-------------\\n");
	deletDate(array, 1);
	foreachArray(array, myPrint);

	printf("-------------\\n");
	struct Person p = {"李",16};
	//调用回调函数比较数据信息
	removeDate(array, &p, mycampar);
	foreachArray(array, myPrint);
	destoryArr(array);
	array = NULL;

}


int main()
{
	test01();
	return 0;
}

下面是程序运行的结果

以上是关于数据结构-C语言实现动态扩容数组的主要内容,如果未能解决你的问题,请参考以下文章

C语言实现动态结构体数组

c语言动态数组如何扩充空间

动态数组的实现(c语言)----数据结构

数据结构初阶:动态顺序表的功能实现(用C语言实现,附图分析)

c语言 动态数组

恋上数据结构手写ArrayList + Java动态扩容分析