C语言数据结构:动态顺序表实现与应用
顺序表
- 顺序表核心逻辑
- 顺序表是基于数组实现的线性表,通过
ArrayList
结构体存储数据(data
指针)、容量(capacity
)和最后一个元素的索引(last
)。核心特点是支持动态扩容(满容量时自动翻倍),并通过索引快速访问元素。 - 数据结构
- 顺序表是基于数组实现的线性表,通过
typedef struct {int capacity; // 顺序表容量int lastIndex; // 最后一个元素的索引(-1表示为空)int* data; // 存储数据的数组指针
} Arra
- 初始化(
initList
)- 校验容量合法性(必须 > 0)。
- 分配结构体和数据数组的内存,初始化
last = -1
(表示空表)。 - 若内存分配失败,释放已分配的空间以避免泄漏。
/*** @brief 初始化顺序表(创建顺序表)* @param capacity 顺序表的初始容量(必须大于0)* @return 成功返回顺序表指针;失败返回NULL*/
ArrayList* initList(int capacity) {// 容量合法性校验if (capacity <= 0) {return NULL;}// 分配顺序表结构体内存ArrayList* list = malloc(sizeof(ArrayList));if (list != NULL) {list->last = -1; // 初始状态:顺序表为空(无元素)// 分配数据存储数组的内存(初始化为0)list->data = calloc(capacity, sizeof(int));if (list->data == NULL) {// 数组内存分配失败时,释放结构体以避免内存泄漏free(list);return NULL;}list->capacity = capacity; // 设置初始容量return list;}return NULL; // 结构体内存分配失败
}
- 插入元素(
insertList
)- 采用头部插入:先将现有元素后移一位,再将新元素放入索引0的位置。
- 自动扩容:当表满时调用
expandList
进行二倍扩容(使用realloc
保留原有数据)。
/*** @brief 向顺序表头部插入元素(前插法)* @param list 顺序表指针* @param data 待插入的元素值* @return 插入成功返回true;失败(如指针为空或扩容失败)返回false*/
bool insertList(ArrayList* list, int data) {// 指针合法性校验if (list == NULL) {return false;}// 若顺序表已满,尝试扩容if (isFull(list) && !expandList(list)) {return false; // 扩容失败,插入失败}// 元素后移:为头部插入腾出位置(从最后一个元素开始移)for (int i = list->last; i >= 0; i--) {list->data[i + 1] = list->data[i];}// 插入新元素到头部(索引0的位置)list->data[0] = data;list->last++; // 更新最后一个元素的索引return true;
}
- 查找元素(
findElement
)- 从顺序表中查找元素, 如果找到元素,则返回
/*** @brief 查找元素在顺序表中的索引* @param list 顺序表指针* @param data 待查找元素* @return 找到返回对应索引,未找到返回-1*/
int findElement(ArrayList* list, int data) {if (list == NULL || isEmpty(list)) {return -1;}for (int i = 0; i <= list->lastIndex; i++) {if (list->data[i] == data) {return i;}}return -1;
}
- 删除元素(
removeElement
)- 先通过
findElement
查找元素索引。 - 若存在,将索引后的元素前移一位,覆盖待删除元素,最后更新
last
索引。
- 先通过
/** @brief 从顺序表中删除指定元素* @param list 顺序表指针* @param data 待删除元素*/
void removeElement(ArrayList* list, int data) {if (list == NULL || isEmpty(list)) {return;}// 查找元素索引int index = findElement(list, data);printf("元素%d在顺序表中的索引为%d\n", data, index);// 若找到则删除if (index != -1) {// 元素前移覆盖待删除元素for (int i = index; i < list->lastIndex; i++) {list->data[i] = list->data[i + 1];}list->lastIndex--; // 更新最后一个元素索引}
}
- 辅助函数
isFull
/isEmpty
:判断表的状态(满/空)。expandList
:动态扩容(核心是realloc
函数的使用)。showList
:遍历打印元素,方便调试和查看表内容。
/*** @brief 判断顺序表是否已满* @param list 顺序表指针* @return 已满返回true,否则返回false*/
static bool isFull(ArrayList* list) {if (list == NULL) return true;// 最后一个元素索引等于容量-1时表示已满return list->lastIndex == list->capacity - 1;
}/*** @brief 对顺序表进行二倍扩容* @param list 顺序表指针* @return 扩容成功返回true,失败返回false*/
static bool expandArrayList(ArrayList* list) {if (list == NULL) return false;// 计算新容量(原容量的2倍)int newCapacity = list->capacity * 2;// 重新分配内存int* newData = realloc(list->data, sizeof(int) * newCapacity);if (newData == NULL) {return false;}// 更新数据指针和容量list->data = newData;// 初始化新增的内存空间memset(&list->data[list->capacity], 0, sizeof(int) * (newCapacity - list->capacity));list->capacity = newCapacity;return true;
}/*** @brief 判断顺序表是否为空* @param list 顺序表指针* @return 为空返回true,否则返回false*/
static bool isEmpty(ArrayList* list) {return list->lastIndex == -1;
}
- 销毁顺序表
** 内存释放步骤 **:
先释放data指向的数组内存(因为数组是动态分配的)
再释放ArrayList结构体本身的内存
最后将外部传入的指针置空(关键:防止后续误用已释放的内存)
函数说明
// 销毁顺序表
destroy_list(&list);
if (list == NULL) {printf("顺序表已成功销毁\n");
}
为什么使用双重指针
因为需要在函数内部将外部的ArrayList*指针置空(避免野指针),单指针无法修改外部指针的指向,所以必须使用ArrayList。
完整代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>// 顺序表结构体定义
typedef struct
{int capacity; // 顺序表容量int last; // 最后一个元素的索引(-1表示空表)int *data; // 存储数据的动态数组
} ArrayList;// 静态辅助函数声明
static bool isFull(ArrayList *list);
static bool expend_list(ArrayList *list);
static bool isEmpty(ArrayList *list);// 函数声明
ArrayList *init_list(int capacity);
bool insert_list(ArrayList *list, int data);
void show(ArrayList *list);
int find_e(ArrayList *list, int data);
void remove_element(ArrayList *list, int data);
void destroy_list(ArrayList **list);/*** @brief 判断顺序表是否已满* @param list 顺序表指针* @return 已满返回true,否则返回false*/
static bool isFull(ArrayList *list)
{if (list == NULL) return true;return list->last == list->capacity - 1;
}/*** @brief 对顺序表进行二倍扩容* @param list 顺序表指针* @return 扩容成功返回true,失败返回false*/
static bool expend_list(ArrayList *list)
{if (list == NULL) return false;int new_len = list->capacity * 2;int *new_int = realloc(list->data, sizeof(int) * new_len);if (new_int == NULL)return false; list->data = new_int;memset(&list->data[list->capacity], 0, sizeof(int) * (new_len - list->capacity));list->capacity = new_len;return true;
}/*** @brief 判断顺序表是否为空* @param list 顺序表指针* @return 为空返回true,否则返回false*/
static bool isEmpty(ArrayList *list)
{return list->last == -1;
}/*** @brief 初始化顺序表* @param capacity 初始容量* @return 成功返回顺序表指针,失败返回NULL*/
ArrayList *init_list(int capacity)
{if (capacity <= 0)return NULL;ArrayList *list = malloc(sizeof(ArrayList));if (list != NULL){list->last = -1;list->data = calloc(capacity, sizeof(int));if (list->data == NULL){free(list);return NULL;}list->capacity = capacity;return list;}return NULL;
}/*** @brief 向前插法插入元素* @param list 顺序表指针* @param data 待插入元素* @return 插入成功返回true,失败返回false*/
bool insert_list(ArrayList *list, int data)
{if (list == NULL) return false;if (isFull(list) && !expend_list(list))return false;// 元素后移,为新元素腾出位置for (int i = list->last; i >= 0; i--){list->data[i + 1] = list->data[i];}list->data[0] = data;list->last++;return true;
}/*** @brief 遍历并打印顺序表元素* @param list 顺序表指针*/
void show(ArrayList *list)
{if (!list || isEmpty(list)){perror("顺序表为空!");return;}printf("顺序表中的元素:\n");for (int i = 0; i <= list->last; i++){printf("%-4d ", list->data[i]);}printf("\n");
}/*** @brief 查找元素位置* @param list 顺序表指针* @param data 待查找元素* @return 找到返回索引,未找到返回-1*/
int find_e(ArrayList *list, int data)
{for (int i = 0; i <= list->last; i++){if (list->data[i] == data)return i;}return -1;
}/*** @brief 删除指定元素* @param list 顺序表指针* @param data 待删除元素*/
void remove_element(ArrayList *list, int data)
{if (list == NULL)return;int index = find_e(list, data);printf("该元素%d在序列中的索引为%d\n", data, index);if (index != -1){// 元素前移覆盖待删除元素for (int i = index; i < list->last; i++){list->data[i] = list->data[i + 1];}list->last--;}
}/*** @brief 销毁顺序表,释放所有内存* @param list 顺序表指针的指针*/
void destroy_list(ArrayList **list)
{if (list == NULL || *list == NULL)return;// 先释放数据数组free((*list)->data);(*list)->data = NULL;// 再释放顺序表结构体free(*list);*list = NULL; // 将外部指针置空,避免野指针
}/*** @brief 主函数:测试顺序表功能*/
int main(int argc, char *argv[])
{// 初始化顺序表,初始容量为6ArrayList *list = init_list(6);if (list == NULL){printf("顺序表初始化失败\n");return 1;}printf("初始化顺序表的容量为%d\n", list->capacity);// 插入9个元素(测试扩容功能)for (int i = 0; i <= 8; i++){int val = i * 2;if (insert_list(list, val)){printf("插入%d成功, 当前容量为%d\n", val, list->capacity);}else{printf("插入%d失败, 当前容量为%d\n", val, list->capacity);}}// 显示插入结果printf("遍历顺序表:\n");show(list);// 删除元素16remove_element(list, 16);// 显示删除后结果printf("删除元素后:\n");show(list);// 销毁顺序表destroy_list(&list);if (list == NULL){printf("顺序表已成功销毁\n");}return 0;
}