动态顺序表的实现

本文介绍了C语言中使用动态数组和顺序表的数据结构实现,包括初始化、扩容、插入、删除和销毁操作,以及如何处理不同类型的动态数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基础版

动态数组存放的是数据本身

1.SeqList.h文件

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef int SqListType;

//定义一个顺序表模板
typedef struct SqList {
	SqListType* data;//顺序表的地址
	int size;//顺序表内有效数据的个数
	int capacity;//顺序表的容量
}SqList;

//初始化顺序表
void InitSqList(SqList* ps);

//检查是否扩容
void CheckCapacity(SqList* ps);

//销毁顺序表,释放动态开辟的空间
void SqListDestory(SqList* ps);

//顺序表的尾插(末尾插入数据)
void SqListPushBack(SqList* ps, SqListType x);

//顺序表的尾删(末尾删除数据)
void SqListPopBack(SqList* ps);

//顺序表内有效数据的打印
void SqListPrint(SqList* ps);

//顺序表的头插(开头插入数据)
void SqListPushFront(SqList* ps, SqListType x);

//顺序表的头删(删除第一个数据)
void SqListPopFront(SqList* ps);

//顺序表的任意下标位置插入
void SqListInsert(SqList* ps, int pos, SqListType x);

//顺序表任意下标位置的删除
void SqListErase(SqList* ps, int pos);

//查找顺序表内元素,找到返回其下标,找不到返回-1
int SqListFind(const SqList* ps, SqListType x);

2.SeqList.c文件

#define _CRT_SECURE_NO_WARNINGS 1
#include "SqList.h"

void InitSqList(SqList* ps)
{
	ps->data = NULL;
	ps->size = ps->capacity = 0;
}

void CheckCapacity(SqList* ps)
{
	if (ps->size == ps->capacity)//当顺序表内有效数据存满容量
	{
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//当原容量是0,则直接开辟4个数据容量,否则直接开辟两倍容量
		SqListType* tmp = (SqListType*)realloc(ps->data, newcapacity * sizeof(SqListType));
		if (tmp == NULL)
		{
			printf("realloc fail\n");
			exit(-1);//若申请空间失败则直接结束程序
		}
		ps->data = tmp;
		ps->capacity = newcapacity;
	}
}

void SqListDestory(SqList* ps)
{
	free(ps->data);
	ps->data = NULL;
    ps->capacity = ps->size = 0;
}

void SqListPushBack(SqList* ps, SqListType x)
{
	////检查是否扩容
	//CheckCapacity(ps);
	//ps->data[ps->size] = x;
	//ps->size++;

	//可以复用SqListInsert函数
	SqListInsert(ps, ps->size, x);
}

void SqListPopBack(SqList* ps)
{
	//assert(ps->size > 0);//顺序表内必须要有有效数据才能删除
	//ps->size--;

	//有了SqListErase,则可以直接复用其代码
	SqListErase(ps, ps->size - 1);
}

void SqListPrint(SqList* ps)
{
	assert(ps->size >= 0);//顺序表内必须要有有效数据才能打印
	int i = 0;
	for (i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->data[i]);
	}
	printf("\n");
}

void SqListPushFront(SqList* ps, SqListType x)
{
	//CheckCapacity(ps);//检查是否扩容
	//int end = ps->size-1;
	//while (end >= 0)
	//{
	//	ps->data[end + 1] = ps->data[end];//下标0后的数据一一往后挪
	//	end--;
	//}
	//ps->data[0] = x;
	//ps->size++;

	//可以直接复用SqListInsert函数
	SqListInsert(ps, 0, x);
}

void SqListPopFront(SqList* ps)
{
	//assert(ps->size > 0);//顺序表内必须有有效数据才能删除
	//int begin = 1;
	//while (begin < ps->size)
	//{
	//	ps->data[begin - 1] = ps->data[begin];
	//	begin++;
	//}
	//ps->size--;

	//有了SqListErase后,就可以直接复用其代码
	SqListErase(ps, 0);
}

void SqListInsert(SqList* ps, int pos, SqListType x)
{
	//要确保pos合法
	////温柔的方法
	//if (pos<0 || pos>ps->size)
	//{
	//	printf("pos invalid\n");
	//	return;
	//}

	//暴力的方法
	assert(pos >= 0 && pos <= ps->size);
	CheckCapacity(ps);//检查是否扩容
	int end = ps->size - 1;
	while (end >= pos)
	{
		ps->data[end + 1] = ps->data[end];
		end--;
	}
	ps->data[pos] = x;
	ps->size++;
}

void SqListErase(SqList* ps, int pos)
{
	assert(ps->size > 0);//顺序表内必须要有有效数据才能删除
	assert(pos >= 0 && pos <= ps->size - 1);//确保pos合法
	int begin = pos + 1;
	while (begin < ps->size)
	{
		ps->data[begin - 1] = ps->data[begin];
		begin++;
	}
	ps->size--;
}

int SqListFind(const SqList* ps, SqListType x)
{
	int i = 0;
	for (i = 0; i < ps->size; i++)
	{
		if (x == ps->data[i])
		{
			return i;
		}
	}
	return -1;
}

3.test.c文件

#define _CRT_SECURE_NO_WARNINGS 1
#include "SqList.h"

SqListTest1()
{
	SqList s;
	InitSqList(&s);
	SqListPushBack(&s, 9);
	SqListPushBack(&s, 10);
	SqListPushBack(&s, 11);
	SqListPushBack(&s, 12);
	SqListPushBack(&s, 13);
	SqListPrint(&s);

	SqListPopBack(&s);
	SqListPrint(&s);

	SqListDestory(&s);
}

SqListTest2()
{
	SqList s;
	InitSqList(&s);
	SqListPushFront(&s, 1);
	SqListPushFront(&s, 2);
	SqListPushFront(&s, 3);
	SqListPrint(&s);

	SqListPopFront(&s);
	SqListPopFront(&s);
	SqListPopFront(&s);
	//SqListPopFront(&s);
	SqListPrint(&s);

	SqListDestory(&s);
}

SqListTest3()
{
	SqList s;
	InitSqList(&s);
	SqListInsert(&s, 0, 1);
	SqListInsert(&s, 0, 2);
	SqListInsert(&s, 0, 3);
	SqListPrint(&s);

	SqListErase(&s, 0);
	SqListPrint(&s);

	int pos = SqListFind(&s, 1);
	if (pos != -1)
	{
		SqListErase(&s, pos);
	}
	SqListPrint(&s);

	SqListDestory(&s);
}


int main()
{
	/*SqListTest1();
	SqListTest2();*/
	SqListTest3();
	return 0;
}

升级版

动态数组存放数据的地址

 由于不知道用户之后要在数组中放什么类型的数据以及不知道数据是在栈区还是堆区,所以动态数组中存放的是数据的地址,用void*接收,因为void*能接收任意类型的指针

dynamicArray.h文件

#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

//设计思想:由于不知道用户具体想存什么类型的数据到动态数组,所以存数据的地址到数组即可,也就是数组的每个元素为void*类型
//记录数组的首元素地址,首元素地址,数组容量即可操作动态数组
typedef struct dynamicArray {
	void** pAddr;//数据地址所在数组的首元素地址
	int size;//存储数据地址的数组有效元素个数
	int capacity;//存储数据地址的数组容量
}dynamicArray;



//初始化动态数组
dynamicArray* init_dynamicArray(int capacity);

//元素插入指定下标位置
void insert_dynamicArray(dynamicArray* array, int pos, void* data);

//遍历数组
void foreach_dynamicArray(dynamicArray* array, void(*myPrint)(void*));//第二个参数为函数指针

//删除指定下标的数据
void eraseByIndex_dynamicArray(dynamicArray* array, int pos);

//删除指定值的数据
void eraseByVal_dynamicArray(dynamicArray* array, void* data, void(*compare)(void*, void*));

//销毁数组,释放空间
void destory_dynamicArray(dynamicArray* array);

dynamicArray.c文件

#define _CRT_SECURE_NO_WARNINGS 1

#include "dynamicArray.h"
//初始化
dynamicArray* init_dynamicArray(int capacity)
{
	if (capacity <= 0)//检查用户输入容量的有效性
	{
		return	NULL;
	}
	dynamicArray* array = malloc(sizeof(dynamicArray));//在堆区创建结构体变量
	if (array == NULL)
	{
		return NULL;
	}
	array->pAddr = malloc(sizeof(void*) * capacity);//在堆区创建数组
	if (array->pAddr == NULL)
		return array;//返回array,方便在此函数外部释放array指向的堆空间
	array->capacity = capacity;
	array->size = 0;

	return array;
}

//指定下标位置插入数据
void insert_dynamicArray(dynamicArray* array, int pos, void* data)
{
	if (array == NULL || data == NULL)
	{
		return;
	}
	if (pos < 0 || pos > array->size)
	{
		pos = array->size;//如果输入的下标无效,则进行尾插
	}
	//检查是否需要扩容
	if (array->size == array->capacity)
	{
		int newcapacity = (array->capacity) * 2;
		void** newspace = realloc(array->pAddr, newcapacity*sizeof(void*));
		if (newspace == NULL)
		{
			return;
		}
		array->pAddr = newspace;
		array->capacity = newcapacity;
	}

	for (int i = array->size - 1; i >= pos; i--)//挪动数据
	{
		array->pAddr[i + 1] = array->pAddr[i];
	}
	array->pAddr[pos] = data;//插入数据
	array->size++;//更新size
}


//遍历数组
void foreach_dynamicArray(dynamicArray* array, void(*myPrint)(void*))//第二个参数是函数指针
{
	if (array == NULL || myPrint == NULL)
	{
		return;
	}
	
	for (int i = 0; i < array->size; i++)
	{
		myPrint(array->pAddr[i]);//回调函数
	}
}

void eraseByIndex_dynamicArray(dynamicArray* array, int pos)
{
	if (array == NULL || pos < 0 || pos >= array->size)//检查参数有效性
	{
		return;
	}

	for (int i = pos; i < array->size - 1 ; i++)
	{
		array->pAddr[i] = array->pAddr[i + 1];//往前挪动数据
	}

	array->size--;//更新size
}

void eraseByVal_dynamicArray(dynamicArray* array, void* data, bool(*compare)(void*, void*))
{
	if (array == NULL || data == NULL || compare == NULL)
		return;
	for (size_t i = 0; i < array->size; i++)
	{
		if (compare(array->pAddr[i], data))//如果找到了要删除的元素,那i就是该元素的下标
		{
			eraseByIndex_dynamicArray(array, i);//直接调用按下标删除的函数即可
			break;//删除一个元素后就跳出循环
		}
	}
}

void destory_dynamicArray(dynamicArray* array)
{
	if (array == NULL)
	{
		return;
	}
	if (array->pAddr != NULL)//先释放内层堆空间
	{
		free(array->pAddr);
		array->pAddr = NULL;
	}
	free(array);//再释放外层堆空间
	array = NULL;
}

test.c文件

#define _CRT_SECURE_NO_WARNINGS 1

#include "dynamicArray.h"

typedef struct Person {//用户想要往数组内存放的数据类型
	char* name;
	int age;
}Person;

void myPrintPerson( void* ptr)//用户自己提供的打印函数
{
	Person* p = ptr;
	printf("姓名:%s\t,年龄:%d\n", p->name, p->age);
}

bool comparePerson(void* ptr1, void* ptr2)//用户自己提供的对比函数
{
	Person* p1 = ptr1;
	Person* p2 = ptr2;
	return (strcmp(p1->name, p2->name) == 0 && p1->age == p2->age);
}
int main()
{
	//初始化数组
	dynamicArray* arr = init_dynamicArray(5);

	//创建Person类型的变量
	Person p1 = { "悟空", 199 };
	Person p2 = { "孙尚香", 19 };
	Person p3 = { "青霞", 18 };
	Person p4 = { "李白", 99 };
	Person p5 = { "红梅", 9 };
	Person p6 = { "八戒", 238 };

	//往数组内插入Person类型的变量
	insert_dynamicArray(arr, 0, &p1);
	insert_dynamicArray(arr, 10, &p2);
	insert_dynamicArray(arr, 0, &p3);
	insert_dynamicArray(arr, 1, &p4);
	insert_dynamicArray(arr, 0, &p5);
	insert_dynamicArray(arr, 3, &p6);
	//预期效果:红梅 青霞 李白 八戒 悟空 孙尚香 
	foreach_dynamicArray(arr, myPrintPerson);
	printf("有效元素个数:%d,容量:%d\n", arr->size, arr->capacity);
	putchar('\n');

	eraseByIndex_dynamicArray(arr, 3);//按下标删除元素
	//预期效果:红梅 青霞 李白 悟空 孙尚香 
	foreach_dynamicArray(arr, myPrintPerson);
	printf("有效元素个数:%d,容量:%d\n", arr->size, arr->capacity);

	putchar('\n');
	Person p = { "青霞", 18 };
	eraseByVal_dynamicArray(arr, &p, comparePerson);//按值删除元素
	//预期效果:红梅 李白 悟空 孙尚香 
	foreach_dynamicArray(arr, myPrintPerson);
	printf("有效元素个数:%d,容量:%d\n", arr->size, arr->capacity);

	destory_dynamicArray(arr);//销毁数组,释放空间
	arr = NULL;//手动置空
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值