数据结构与算法【C语言】
一、线性表-动态扩容数组
优点:可按数据元素的序号随机存储,也即可按照索引快速查找某元素。
缺点:插入和删除操作耗时。
动态扩容原理:
1) 若arr->size >= arr->capacity, 则开辟比capacity更大的空间;
2) 将原空间中的元素拷贝至新的空间;
3) 释放旧空间后,链表中的数据指针指向新开辟的空间。
1.1 仅存放整型元素的动态数组
实现功能:
1) 初始化动态数组-DynamicArray* Init_DynamicArray();
2) 向动态数组中插入整型元素-void InsertValue_DynamicArray(DynamicArray arr, int data);
3) 删除指定位置的元素/删除特定值的元素- void RemoveValueByValue_DynamicArray(DynamicArray arr, int pos);/void RemoveValueByValue_DynamicArray(DynamicArray* arr, int data);
4) 返回动态数组的元素个数和容量 int Size_DynamicArray(DynamicArray* arr);/int Capacity_DynamicArray(DynamicArray* arr);
5) 释放动态数组- void FreeSpace_DynamicArray(DynamicArray* arr);
1.1.1 存放整型元素的动态数组存储结构
仅存放整型元素的动态数组存储结构如下图所示,用链表维护动态数组的相关属性,如动态数组中的元素个数(DynamicArray.size)和动态数组的容量(DynamicArray.capacity)。
1.1.2 代码
// 头文件DynamicArray.h
#ifndef DYNAMICARRAY_H
#define DYNAMICARRAY_H
#include<stdlib.h>
#include<stdio.h>
// 用链表维护动态扩容数组的相关参数
typedef struct DYNAMICARRAY {
// 用指针数组存放数据的地址
int* addr;
// 元素的个数
int size;
// 动态数组的容量
int capacity;
}DynamicArray;
// 动态数据组初始化
DynamicArray* Init_DynamicArray();
// 插入数据
void InsertValue_DynamicArray(DynamicArray *arr, int data);
// 删除指定位置的数据
void RemoveValueByPos_DynamicArray(DynamicArray* arr, int pos);
// 根据值删除元素
void RemoveValueByValue_DynamicArray(DynamicArray* arr, int data);
// 返回数组的容量和尺寸
int Size_DynamicArray(DynamicArray* arr);
// 返回数组的容量
int Capacity_DynamicArray(DynamicArray* arr);
// 打印数组
void Print_DynamicArray(DynamicArray* arr);
// 清空数组
void Clear_DynamicArray(DynamicArray* arr);
// 释放数组内存
void FreeSpace_DynamicArray(DynamicArray* arr);
#endif // !DYNAMICARRAY_H
// DynamicArray.c
// DynamicArray.c为头文件DynamicArray.h中所申明函数的实现。
#include"DynamicArray.h"
// 动态数据组初始化
DynamicArray* Init_DynamicArray() {
// 初始化动态数组
DynamicArray* arr = (DynamicArray*)malloc(sizeof(DynamicArray));
if (arr == NULL) {
printf("内存分配失败!");
return NULL;
}
else {
arr->size = 0;
arr->capacity = 10;
arr->addr = (int*)malloc(sizeof(int) * arr->capacity);
return arr;
}
}
// 插入数据
void InsertValue_DynamicArray(DynamicArray* arr, int data) {
// 判断边界情况
if (arr == NULL) {
printf("数组arr为空,请初始化动态数组: arr\n");
return;
}
// 如果当前元素个数超出动态数组的容量
if (arr->size >= arr->capacity) {
// 更新动态数组的容量
arr->capacity = arr->capacity * 2;
// 开辟新空间
int* temp = (int*)malloc(sizeof(int) * arr->capacity);
// 将旧空间的元素拷贝至新开辟的空间中
for (int i = 0; i < arr->size; i++) {
temp[i] = arr->addr[i];
}
// 销毁旧空间
free(arr->addr);
arr->addr = temp;
}
// 插入新的元素
arr->addr[arr->size] = data;
arr->size++;
}
// 删除指定位置的数据
void RemoveValueByPos_DynamicArray(DynamicArray* arr, int pos) {
// 判断数组arr,和位置pos是否合法
if (arr == NULL || pos >= arr->size || pos < 0) {
// 传入参数有误
printf("传入参数有误,请检查参数\n");
return;
}
// 将pos位置后的元素往前移动
for (int i = pos; i < arr->size - 1; i++) {
arr->addr[i] = arr->addr[i + 1];
}
// arr->size--
arr->size--;
}
// 根据传入的值删除数据
void RemoveValueByValue_DynamicArray(DynamicArray* arr, int data) {
// 判断数组arr,和位置pos是否合法
if (arr == NULL) {
// 传入参数有误
printf("传入参数有误,请检查参数\n");
return;
}
// 找到arr中第一个元素为data的位置
for (int i = 0; i < arr->size; i++) {
if (arr->addr[i] == data) {
if (i == arr->size - 1) {
arr->size--;
return;
}
else {
for (int j = i; j < arr->size - 1; j++) {
arr->addr[j] = arr->addr[j + 1];
}
arr->size--;
return;
}
}
}
// arr->size--
}
// 返回数组的容量和尺寸
int Size_DynamicArray(DynamicArray* arr) {
// 判断边界情况
if (arr == NULL) {
printf("数组为空,请初始化数组\n");
return -1;
}
return arr->size;
}
// 返回数组的容量
int Capacity_DynamicArray(DynamicArray* arr) {
// 判断边界情况
if (arr == NULL) {
printf("数组为空,请初始化数组\n");
return -1;
}
return arr->capacity;
}
// 打印数组
void Print_DynamicArray(DynamicArray* arr) {
if (arr == NULL) return;
for (int i = 0; i < arr->size; i++) {
printf("%d ", arr->addr[i]);
}
printf("\n");
}
// 清空数组
void Clear_DynamicArray(DynamicArray* arr) {
if (arr == NULL) return;
arr->size = 0;
}
// 释放数组内存
void FreeSpace_DynamicArray(DynamicArray* arr) {
if (arr == NULL) return;
// 由内往外释放
if (arr->addr) free(arr->addr);
free(arr);
}
// main.c 完成对动态数组中相关功能的测试。
#include"DynamicArray.h"
#include <time.h>
#define MAX_NUM 10
void GenRandomData(DynamicArray *arr, int len) {
if (arr == NULL || len < 0) return;
srand((unsigned int)time(NULL));
for (int i = 0; i < len; i++) {
InsertValue_DynamicArray(arr, rand() % len);
}
}
int main() {
// 初始化数组
DynamicArray* arr = Init_DynamicArray();
// 向动态数组中插入元素
/*for (int i = 0; i < 20; i++) {
InsertValue_DynamicArray(arr, i);
}*/
// 随机插入元素
GenRandomData(arr, MAX_NUM);
// 删除前的数组
printf("根据位置删除前的数组:\n");
Print_DynamicArray(arr);
// 根据位置删除元素
RemoveValueByPos_DynamicArray(arr, 8);
// 删除后的数组
printf("根据位置删除后的数组:\n");
Print_DynamicArray(arr);
// 根据值删除数组
RemoveValueByValue_DynamicArray(arr, 0);
printf("根据元素删除后的数组:\n");
Print_DynamicArray(arr);
printf("\n=====================\n");
// 数组大小
printf("数组元素个数:%d\n", Size_DynamicArray(arr));
// 数组容量
printf("数组容量:%d\n", Capacity_DynamicArray(arr));
// 清空数组
Clear_DynamicArray(arr);
printf("\n清空后的动态数组:");
// 数组大小
printf("数组元素个数:%d\n", Size_DynamicArray(arr));
// 数组容量
printf("数组容量:%d\n", Capacity_DynamicArray(arr));
// 释放空间
FreeSpace_DynamicArray(arr);
return 0;
}
1.1.3 运行结果
运行结果如下所示,
1.2 存放任意类型数据的动态数组
1.2.1 存放任意类型数据的动态数组存储结构
在1.1小节上进行扩展:实现线性存储任意类型的数据的动态数组,其存储的数据结构如下图所示,ArrayNode结点的viod*data可以指向任意类型的数据。
1.2.2 代码
// 头文件DynamicArray.h
#ifndef DYNAMICARRAY_H
#define DYNAMICARRAY_H
#include<stdlib.h>
#include<stdio.h>
// 打印函数-由用户自己提供打印的接口,
typedef void (*PRINT)(void*);
// 比较函数
typedef int (*COMPARE)(void*, void*);
typedef struct ARRAYNODE {
void* data;
}ArrayNode;
// 用链表维护动态扩容数组的相关参数
typedef struct DYNAMICARRAY {
// 用指针数组存放数据的地址
ArrayNode* arrNode;
//void* data;
// 元素的个数
int size;
// 动态数组的容量
int capacity;
}DynamicArray;
// 动态数据组初始化
DynamicArray* Init_DynamicArray();
// 插入数据
void InsertData_DynamicArray(DynamicArray* arr, void* data);
// 删除指定位置的数据
void RemoveValueByPos_DynamicArray(DynamicArray* arr, int pos);
// 根据值删除元素
void RemoveValueByValue_DynamicArray(DynamicArray* arr, void* data, COMPARE compare);
// 返回数组的容量和尺寸
int Size_DynamicArray(DynamicArray* arr);
// 返回数组的容量
int Capacity_DynamicArray(DynamicArray* arr);
// 打印数组
void Print_DynamicArray(DynamicArray* arr, PRINT print);
// 清空数组
void Clear_DynamicArray(DynamicArray* arr);
// 释放数组内存
void FreeSpace_DynamicArray(DynamicArray* arr);
#endif // !DYNAMICARRAY_H
// DynamicArray.c
#include"DynamicArray.h"
// 动态数据组初始化
DynamicArray* Init_DynamicArray() {
// 初始化动态数组
DynamicArray* arr = (DynamicArray*)malloc(sizeof(DynamicArray));
if (arr == NULL) {
printf("内存分配失败!\n");
return NULL;
}
else {
arr->size = 0;
arr->capacity = 3;
arr->arrNode = (ArrayNode*)malloc(sizeof(ArrayNode) * arr->capacity);
return arr;
}
}
// 插入数据
void InsertData_DynamicArray(DynamicArray* arr, void* data) {
// 判断边界情况
if (arr == NULL) {
printf("数组arr为空,请初始化动态数组: arr\n");
return;
}
// 如果当前元素个数超出动态数组的容量
if (arr->size >= arr->capacity) {
// 更新容量
arr->capacity = arr->capacity * 2;
// 开辟新空间
ArrayNode* NewArrayNode = (ArrayNode*)malloc(sizeof(ArrayNode) * arr->capacity );
// 将旧空间的元素拷贝至新开辟的空间中
for (int i = 0; i < arr->size; i++) {
NewArrayNode[i].data = arr->arrNode[i].data;
}
// 销毁旧空间
free(arr->arrNode);
arr->arrNode = NewArrayNode;
}
// 插入新的元素
arr->arrNode[arr->size].data = data;
arr->size++;
}
// 删除指定位置的数据
void RemoveValueByPos_DynamicArray(DynamicArray* arr, int pos) {
// 判断数组arr,和位置pos是否合法
if (arr == NULL || pos >= arr->size || pos < 0) {
// 传入参数有误
printf("传入参数有误,请检查参数\n");
return;
}
// 将pos位置后的元素往前移动
for (int i = pos; i < arr->size - 1; i++) {
arr->arrNode[i].data = arr->arrNode[i + 1].data;
}
// arr->size--
arr->size--;
}
// 根据传入的值删除数据
void RemoveValueByValue_DynamicArray(DynamicArray* arr, void* data, COMPARE compare) {
// 判断数组arr,和位置pos是否合法
if (arr == NULL) {
// 传入参数有误
printf("传入参数有误,请检查参数\n");
return;
}
// 找到arr中第一个元素为data的位置
for (int i = 0; i < arr->size; i++) {
if (compare(arr->arrNode[i].data, data) == 1) {
if (i == arr->size - 1) {
arr->size--;
return;
}
else {
for (int j = i; j < arr->size-1; j++) {
arr->arrNode[j].data = arr->arrNode[j + 1].data;
}
// 元素个数减1
arr->size--;
return;
}
}
}
}
// 返回数组的容量和尺寸
int Size_DynamicArray(DynamicArray* arr) {
// 判断边界情况
if (arr == NULL) {
printf("数组为空,请初始化数组\n");
return -1;
}
return arr->size;
}
// 返回数组的容量
int Capacity_DynamicArray(DynamicArray* arr) {
// 判断边界情况
if (arr == NULL) {
printf("数组为空,请初始化数组\n");
return -1;
}
return arr->capacity;
}
// 打印数组
void Print_DynamicArray(DynamicArray* arr, PRINT print) {
if (arr == NULL) return;
for (int i = 0; i < arr->size; i++) {
print(arr->arrNode[i].data);
}
printf("\n");
}
// 清空数组
void Clear_DynamicArray(DynamicArray* arr) {
if (arr == NULL) return;
arr->size = 0;
}
// 释放数组内存
void FreeSpace_DynamicArray(DynamicArray* arr) {
if (arr == NULL) return;
// 由内往外释放
if (arr->arrNode) free(arr->arrNode);
free(arr);
}
// main.c
#include"DynamicArray.h"
#include<string.h>
#include <time.h>
#define MAX_NUM 10
typedef struct PERSON {
char name[64];
int age;
}Person;
// 打印函数
void myPrint(void* data) {
Person* p = (Person*)data;
printf("姓名:%s,年龄:%d\n", p->name, p->age);
}
// 比较函数, data1为动态数组中的元素,data2为用户传入的数据
int compare(void* data1, void* data2) {
Person* p1 = (Person*)data1;
Person* p2 = (Person*)data2;
// 如果姓名和年龄均相等,则判定data1与data2相等
if (!strcmp(p1->name, p2->name) && p1->age == p2->age) return 1;
return -1;
}
int main() {
// 初始化结构体
Person p1 = { "aaa", 20 }, p2 = { "bbb", 30 }, p3 = { "ccc", 40 }, p4 = {"ddd", 50};
// 初始化数组
DynamicArray* arr = Init_DynamicArray();
// 插入结构体数据
InsertData_DynamicArray(arr, &p1);
InsertData_DynamicArray(arr, &p2);
InsertData_DynamicArray(arr, &p3);
InsertData_DynamicArray(arr, &p4);
// 删除前的数组
printf("删除前的数组:\n");
Print_DynamicArray(arr, myPrint);
// 根据位置删除元素
//RemoveValueByPos_DynamicArray(arr, 0);
// 删除后的数组
printf("根据位置删除后的数组:\n");
Print_DynamicArray(arr, myPrint);
// 根据值删除数组
Person p5 = {"ddd", 50};
RemoveValueByValue_DynamicArray(arr, &p5, compare);
printf("根据元素删除后的数组:\n");
Print_DynamicArray(arr, myPrint);
printf("\n=====================\n");
// 数组大小
printf("数组元素个数:%d\n", Size_DynamicArray(arr));
// 数组容量
printf("数组容量:%d\n", Capacity_DynamicArray(arr));
Clear_DynamicArray(arr);
printf("\n清空后的动态数组:");
// 数组大小
printf("数组元素个数:%d\n", Size_DynamicArray(arr));
// 数组容量
printf("数组容量:%d\n", Capacity_DynamicArray(arr));
// 释放空间
FreeSpace_DynamicArray(arr);
return 0;
}
1.2.3 运行结果
向动态数组中插入结构体数据,运行结果如下所示,
二、参考链接
1、数据结构与算法知识点总结
2、C++教程