数据结构基础
1.1 数据结构基础
算法 + 数据结构 = 程序
宏观定义:数据结构是为了高效访问数据而设计出的一种数据的组织和存储方式。
微观定义:数据结构是相互之间存在一种或多种特定关系的数据元素的集合。
数据结构本身和编程语言无关。

内存:由许多存储单元组成,每个存储单元可以存储一个固定大小的数据块,通常以字节 (Byte) 为单位。每个存储单元都有一个唯一的地址,操作系统根据这一地址去访问内存中的数据。
介绍数据结构基础,涵盖逻辑结构与存储结构。详细讲解了动态数组、单链表、顺序栈及循环队列的定义、优缺点及 C 语言代码实现,包括初始化、增删改查等操作。内容修正了原文本中的术语错误与代码格式问题,提供了完整的可运行示例。
算法 + 数据结构 = 程序
宏观定义:数据结构是为了高效访问数据而设计出的一种数据的组织和存储方式。
微观定义:数据结构是相互之间存在一种或多种特定关系的数据元素的集合。
数据结构本身和编程语言无关。

内存:由许多存储单元组成,每个存储单元可以存储一个固定大小的数据块,通常以字节 (Byte) 为单位。每个存储单元都有一个唯一的地址,操作系统根据这一地址去访问内存中的数据。
数据结构中的数据元素就保存在内存中,这些数据元素可能存储在连续的内存单元中,或存储在分散的内存单元中。

顺序存储结构 把逻辑上相邻的数据元素存储在物理位置上也相邻的存储单元中,即所有的元素依次存放在一片连续的存储空间中。
链式存储结构 把逻辑是相邻的数据元素存储在物理上可以不相邻的存储空间中,每个元素拥有一个结点,结点中除了存放数据本身以外,还存放指向下一个结点的指针。
索引存储结构 即目录,索引存储在存储数据元素之外,同时建立数据元素的目录,方便快速检索。

// 根据数据元素的关键字直接计算出该元素的关键码,由关键码决定存储地址,又称为 Hash 存储。
// 散列表的实现原理是映射,在映射的过程中,事先设定的函数称为散列函数或者哈希函数。
包括:①分配资源、建立结构、释放资源;②插入和删除;③获取和遍历;④修改和排序。
C 语言中,数组是具有相同类型的数据元素的集合,是最常见、最简单的一种数据结构。 在 C 语言中,数组的长度一旦确定就不可更改了。
优点:
缺点:
定义动态数组的结构体,其中包含指向数组的指针、数组的容量 (可以容纳的最大元素个数)、数组中实际的元素个数。
// 通过 typedef 给类型取别名
typedef int element_t;
// 定义结构体,表示动态数组
typedef struct {
element_t* data; // 指向动态数组的指针
size_t capacity; // 数组容量 (最大允许的元素个数)
size_t length; // 数组长度 (数组中已添加的元素个数)
} DynamicArray;
initDynamicArray 函数初始化动态数组并设定数组容量,根据传入的 initCapacity 的值计算所需的内存,通过 malloc 给数组 array 分配内存空间,数组 array 的容量即为 initCapacity,同时将数组 array 的长度初始化为 0。
/**
* @brief 初始化动态数组,分配内存空间
* @param DynamicArray *array 要进行初始化的动态数组
* @param size_t initCapacity 初始容量
*/
void initDynamicArray(DynamicArray *array, size_t initCapacity) {
// 动态分配内存
array->data = (element_t*)malloc(sizeof(element_t) * initCapacity);
array->capacity = initCapacity;
array->length = 0;
}
resizeDynamicArray 函数调整动态数组的内存大小,将指向数组 array 的指针和要扩大的倍数 (一般为 2 的整数倍) newCapacity 传入函数,先计算所需的内存大小,通过 realloc 函数重新给数组分配内存大小,将 newCapacity 赋给数组的容量参数。
/**
* @brief 调整动态数组内存大小
* @param DynamicArray *array 动态数组
* @param size_t newCapacity 新的容量 (元素个数)
*/
void resizeDynamicArray(DynamicArray *array, size_t newCapacity) {
// 扩容
array->data = (element_t*)realloc(array->data, sizeof(element_t) * newCapacity);
// 改容量
array->capacity = newCapacity;
}
insertAt 函数在指定位置增加数组元素。
index 超过数组的长度 array->length,则不合理。resizeDynamicArray 函数扩容。index 处时,将要插入的元素 element 放入到 array->data[index] 中。/**
* @brief 在指定位置插入新元素
* @param DynamicArray *array 动态数组
* @param size_t index 位置(下标)
* @param element_t element 新元素
*/
void insertAt(DynamicArray *array, size_t index, element_t element) {
// 判断位置是否合理
if (index > array->length) {
printf("%zu 位置不合理!!!\n", index);
return;
}
// 判断容量是否足够
if (array->length == array->capacity) {
// 扩容
resizeDynamicArray(array, array->capacity * 2);
}
// 插入元素
// 插入位置后面的元素整体向后移动
// 倒序:从数组中最后一个元素位置 +1 开始,循环到 index 为止,每次将当前位置前面的元素往当前位置放
for (size_t i = array->length; i > index; i--) {
// 移动元素
array->data[i] = array->data[i - 1];
}
// 将插入的元素放到 index 位置
array->data[index] = element;
// 数组长度自增
array->length++;
}
insertEnd 函数在末尾插入元素,则直接调用 insertAt 函数,传入数组的长度,即为数组末尾。
/**
* @brief 在末尾插入新元素
* @param DynamicArray *array 动态数组
* @param element_t element 新元素
*/
void insertEnd(DynamicArray *array, element_t element) {
insertAt(array, array->length, element);
}
getLength 函数获取动态数组的长度,即直接返回数组的长度参数。
/**
* @brief 获取动态数组长度(元素个数)
* @param const DynamicArray *array
* @return size_t 数组长度
*/
size_t getLength(const DynamicArray *array) {
return array->length;
}
deleteAt 函数删除指定位置的元素并将被删除的元素保存。
index 大于数组最后一个元素的下标,则删除位置不合理。array->data[index] 赋给指针 *delete_element。index 处开始之后的元素都往前移动一个元素单位。/**
* @brief 删除指定位置的元素并将被删除的元素保存到第三个参数
* @param DynamicArray *array 动态数组
* @param size_t index 删除的位置(下标)
* @param element_t *deleted_element 用于保存被删除的元素
* @return bool 是否删除成功 true(1) 成功 false(0) 失败
*/
bool deleteAt(DynamicArray *array, size_t index, element_t *delete_element) {
// 判断位置是否合理
if (index > array->length - 1) {
return false;
}
// 记录被删除元素
*delete_element = array->data[index];
// 删除
// 从 index 位置开始,循环到小于长度 -1 的位置,每次将当前位置后面的元素往当前位置放
for (; index < array->length - 1; index++) {
array->data[index] = array->data[index + 1];
}
// 长度自减
array->length--;
return true;
}
deleteEnd 函数删除末尾的元素并将被删除的元素保存,这直接调用 deleteAt 函数,传入最后一个元素的下标 array->length-1 即可。
/**
* @brief 删除末尾的元素并将被删除的元素保存到第二个参数
* @param DynamicArray *array 动态数组
* @param element_t *deleted_element 用于保存被删除的元素
* @return bool 是否删除成功 true(1) 成功 false(0) 失败
*/
bool deleteEnd(DynamicArray *array, element_t *delete_element) {
return deleteAt(array, array->length - 1, delete_element);
}
modifyAt 函数修改指定位置的元素值,首先判断修改位置是否合理,再直接将新的值 element 赋给 array->data[index]。
/**
* @brief 指定位置修改
* @param DynamicArray *array 动态数组
* @param size_t index 修改位置
* @param size_t element 新的值
*/
void modifyAt(DynamicArray *array, size_t index, element_t element) {
// 判断位置是否合理
if (index >= array->length) {
printf("%zu 位置不合理!!!", index);
return;
}
// 修改
array->data[index] = element;
}
printfDynamicArray 函数遍历数组,先输出当前数组的容量和长度,再通过 for 循环从头开始遍历数组。
/**
* @brief 遍历所有的元素
* @param DynamicArray *array
*/
void printfDynamicArray(DynamicArray *array) {
printf("容量:%zu , 长度:%zu, 元素:", array->capacity, array->length);
for (size_t i = 0; i < array->length; i++) {
printf("%d ", array->data[i]);
}
printf("\n\n");
}
freeDynamicArray 函数释放动态数组的内存,直接利用 free 函数释放内存,然后再将数组的参数重置。
/**
* @brief 释放动态数组内存
* @param DynamicArray *array 动态数组
*/
void freeDynamicArray(DynamicArray *array) {
// 释放内存
free(array->data);
// 重置成员值
array->data = NULL;
array->capacity = 0;
array->length = 0;
}
int main() {
// 声明动态数组
DynamicArray array;
// 初始化动态数组
initDynamicArray(&array, 8);
// 末尾插入元素
insertEnd(&array, 100);
insertEnd(&array, 200);
insertEnd(&array, 300);
insertEnd(&array, 400);
insertEnd(&array, 500);
insertEnd(&array, 600);
printfDynamicArray(&array);
// 插入元素
insertAt(&array, 2, 250);
printfDynamicArray(&array);
// 删除末尾元素
element_t delete_element;
deleteEnd(&array, &delete_element);
printf("%d 被删除了\n", delete_element);
printfDynamicArray(&array);
// 指定位置删除
deleteAt(&array, 2, &delete_element);
printf("%d 被删除了\n", delete_element);
printfDynamicArray(&array);
// 指定位置修改
modifyAt(&array, 1, 20000);
printfDynamicArray(&array);
return 0;
}

分为单链表、双链表、循环单链表、循环双链表。

优点:
缺点:
Node 包含结点数据 data 和下一个结点的地址 Node *next。LinkedList 包含头结点地址和链表实际长度。// 定义结点中维护的数据类型
typedef int element_t;
// 定义结构体,表示链表中的结点
typedef struct Node {
element_t data; // 结点中的数据
struct Node* next; // 下个结点的地址
} Node;
// 定义结构体,表示链表
typedef struct {
Node *head; // 头结点地址
size_t length; // 链表的长度 (已添加的结点的个数)
} LinkedList;
initLinkedList 函数初始化链表,即头结点地址为 NULL,链表长度为 0。
/**
* @brief 初始化链表
* @param LinkedList *list 链表
*/
void initLinkedList(LinkedList *list) {
list->head = NULL;
list->length = 0;
}
getLength 函数返回链表的长度,直接通过 list->length 访问链表的长度参数。
/**
* @brief 返回链表的长度
* @param const LinkedList *list
* @return size_t 链表的长度
*/
size_t getLength(const LinkedList *list) {
return list->length;
}
getPrevNode 函数返回指定位置的上一个结点,将链表的头结点 list->head 赋给一个新结点,再利用 for 循环,从链表第二个结点开始一直循环到指定的下标 index 处。
/**
* @brief 返回指定位置的上一个结点
* @param LinkedList *list
* @param size_t index
* @return Node * 指定位置的上一个结点的地址
*/
Node *getPrevNode(LinkedList *list, size_t index) {
Node *current_node = list->head;
for (size_t i = 1; i < index; i++) {
// 注意:若改为 i=0 则函数返回的是指定位置的结点
current_node = current_node->next;
}
return current_node;
}

insertAt 函数element,指向下一个结点的指针为 NULL。list->head 赋给新结点连接下一个结点的指针 new_node->next,把新结点的地址 new_node 赋给头结点地址 list->head。getPrevNode 函数找到需要插入位置的上一个结点的地址,将返回的上一个结点的地址赋给一个 Node *current_node 存储,current_node->next 的下一个结点的参数赋给新结点中指向下一个结点的参数,再将新结点赋给 current_node->next。/**
* @brief 在指定位置插入新结点
* @param LinkedList *list 链表
* @param size_t index 位置(下标从 0 开始)
* @param element_t element 新结点的数据值
*/
void insertAt(LinkedList *list, size_t index, element_t element) {
// 判断插入位置是否合理
if (index > list->length) {
printf("%zu 位置不合理!!!\n", index);
return;
}
// 动态分配内存
Node *new_node = (Node *)malloc(sizeof(Node));
// 给结点的成员赋值
new_node->data = element;
new_node->next = NULL;
// 分两种情况:
if (index == 0) {
// 作为头结点插入,新节点将成为链表的新头节点。它的 next 指针指向当前的头节点,然后将链表的头指针更新为新节点。
// 将 head 的值赋值给 new_node 的 next
new_node->next = list->head;
// 在将 new_node 赋值给 head
list->head = new_node;
} else {
Node *current_node = getPrevNode(list, index);
// 修改指向
// 将 current_node 的 next 赋值给 new_node 的 next
new_node->next = current_node->next;
// 再将 new_node 赋值给 current_node 的 next
current_node->next = new_node;
}
// 长度自增
list->length++;
}

insertEnd 函数直接调用 insertAt 函数。
/**
* @brief 在末尾插入结点
* @param LinkedList *list 链表
* @param element_t element 新结点的数据值
*/
void insertEnd(LinkedList *list, element_t element) {
insertAt(list, list->length, element);
}
deleteAt 函数getPrevNode 函数找到需要插入位置的上一个结点的地址,将返回的上一个结点的地址 current_node,保存被删除结点的地址和值,再将被删除结点的下一个结点的地址 current_node->next->next 赋给被删除结点的上一个结点中指向下一个结点地址的参数 current_node->next。free 函数释放删除的结点内存。/**
* @brief 删除指定位置的结点,并将删除的结点的数据值保存到第三个参数中
* @param LinkedList *list 链表
* @param size_t index 删除位置(索引)
* @param element_t *deleted_element 用于存储被删除的结点的数据值
* @return bool 是否删除成功,1 表示成功,0 表示删除失败
*/
bool deleteAt(LinkedList *list, size_t index, element_t *delete_element) {
// 判断删除的位置是否合理
if (index >= list->length) {
return false;
}
// 定义指针,保存被删除结点的地址
Node *delete_node;
// 分两种情况:
if (index == 0) {
// 删除头结点
// 记录被删除结点
delete_node = list->head;
// 保存被删除的结点的值
*delete_element = list->head->data;
// 修改指向
list->head = list->head->next;
} else {
Node *current_node = getPrevNode(list, index);
// 记录被删除的结点
delete_node = current_node->next;
// 保存将被删除节点的值
*delete_element = current_node->next->data;
// 修改指向
current_node->next = current_node->next->next;
}
// 释放结点的内存
free(delete_node);
// 长度自减
list->length--;
return true;
}
deleteEnd 函数删除末尾结点,直接利用 deleteAt 函数。
/**
* @brief 删除末尾元素
* @param LinkedList *list 链表
* @param element_t *deleted_element 用于存储被删除的结点的数据值
* @return bool 是否删除成功,1 表示成功,0 表示删除失败
*/
bool deleteEnd(LinkedList *list, element_t *delete_element) {
return deleteAt(list, list->length - 1, delete_element);
}
getValue 函数获取指定位置的结点数据值。
list->head->data。getPrevNode 函数找到需要插入位置的上一个结点的地址,将返回的结点的地址 prev_node,在访问返回结点的下一个结点的数据值 prev_node->next->data。/**
* @brief 获取指定位置的结点的数据值
* @param LinkedList *list 用获取的链表
* @param size_t index 获取的下标
* @param element_t *element 用于存储结点的数据值
*/
void getValue(LinkedList *list, size_t index, element_t *element) {
// 判断位置是否合理
if (index >= list->length) {
printf("%zu 位置不合理!!! \n", index);
return;
}
// 分两种情况:
if (index == 0) {
// 头结点的值
*element = list->head->data;
} else {
// 其他结点的值
Node *prev_node = getPrevNode(list, index);
*element = prev_node->next->data;
}
}
setValue 函数修改指定位置结点的数据值。
list->head->data。getPrevNode 函数找到需要插入位置的上一个结点的地址,将返回的结点的地址 prev_node,再把修改的新值赋给返回的结点的下一个结点的数据参数 prev_node->next->data。/**
* @brief 修改指定位置的结点的数据值
* @param LinkedList *list 链表
* @param size_t index 位置
* @param element_t new_value 新的数据值
*/
void setValue(LinkedList *list, size_t index, element_t new_value) {
// 判断位置是否合理
if (index >= list->length) {
printf("%zu 位置不合理!!! \n", index);
return;
}
// 分两种情况:
if (index == 0) {
// 头结点的值
list->head->data = new_value;
} else {
// 其他结点的值
Node *prev_node = getPrevNode(list, index);
prev_node->next->data = new_value;
}
}
freeLinkedList 函数释放链表内存,利用 while 循环,只要当前结点 current_node 的参数不为空,就保存当前结点的地址 delete_node,再把当前结点的下一个结点的地址 current_node->next 赋给当前结点 current_node,再利用 free 函数释放保存的地址 delete_node,最后重置链表的值和链表的长度。
/**
* @brief 释放链表内存
* @param LinkedList *list 链表
*/
void freeLinkedList(LinkedList *list) {
// 从头结点开始
Node *current_node = list->head;
Node *delete_node;
while (current_node != NULL) {
// 保存被释放的结点的地址
delete_node = current_node;
// 修改指向
current_node = current_node->next;
// 释放当前结点
free(delete_node);
}
// 重置链表的值
list->head = NULL;
list->length = 0;
}
printfLinkedList 函数打印链表,利用 while 循环,只要当前结点 current_node 的参数不为空,就说明当前结点不是最后一个结点,就输出当前结点的信息。
/**
* @brief 打印链表
* @param LinkedList *list 链表
*/
void printfLinkedList(LinkedList *list) {
printf("长度:%zu \n", list->length);
printf("head: %p \n", list->head); // 头结点
Node *current_node = list->head;
while (current_node != NULL) {
printf("[当前节点地址:%p , 当前结点的值:%d , 下个结点的地址:%p] \n", current_node, current_node->data, current_node->next);
// 指向下个结点
current_node = current_node->next;
}
printf("\n\n");
}
int main() {
// 声明链表
LinkedList list;
// 初始化链表
initLinkedList(&list);
// 末尾插入
insertEnd(&list, 100);
insertEnd(&list, 200);
insertEnd(&list, 300);
insertEnd(&list, 400);
insertEnd(&list, 500);
printfLinkedList(&list);
// 指定位置插入
insertAt(&list, 0, 50);
printfLinkedList(&list);
insertAt(&list, 3, 250);
printfLinkedList(&list);
// 删除尾结点
element_t delete_element;
deleteEnd(&list, &delete_element);
printf("%d 被删除了!!! \n", delete_element);
printfLinkedList(&list);
// 指定位置删除
deleteAt(&list, 3, &delete_element);
printf("%d 被删除了!!! \n", delete_element);
printfLinkedList(&list);
// 获取指定位置结点的值
element_t element;
getValue(&list, 2, &element);
printf("index = 2 的结点的值:%d \n", element);
getValue(&list, 0, &element);
printf("index = 0 的结点的值:%d \n", element);
// 设置指定位置结点的值
setValue(&list, 0, 50000);
setValue(&list, 2, 20000);
printfLinkedList(&list);
// 释放
freeLinkedList(&list);
return 0;
}
栈 (stack),是限制在只能在表的一端进行插入和删除操作的线性表。
特点:后进先出 (LIFO,Last In First Out) 或先进后出 (FILO,First In Last Out) 的线性表。
相关概念:
栈的存储结构:顺序栈、链式栈。
定义结构体 Stack,其中包括栈元素地址的指针 data、栈容量 capacity、栈长度 length。
// 定义栈中元素的类型
typedef int element_t;
// 定义结构体,表示栈
typedef struct {
element_t* data; // 内存空间地址
size_t capacity; // 栈容量 (允许的最大元素个数)
size_t length; // 栈长度 (已入栈的元素个数)
} Stack;
initStack 函数初始化栈,利用 malloc 函数根据传入的初始化容量 initCapacity 计算分配内存的大小,初始化栈容量和栈长度。
/**
* @brief 初始化栈
* @param Stack *stack
* @param size_t initCapacity
*/
void initStack(Stack *stack, size_t initCapacity) {
// 动态分配内存
stack->data = (element_t*)malloc(sizeof(element_t) * initCapacity);
// 其他成员赋值
stack->capacity = initCapacity;
stack->length = 0;
}
getLength 函数直接访问栈的长度参数 stack->length。
/**
* @brief 返回栈内元素个数
* @param const Stack *stack
* @return size_t 元素个数
*/
size_t getLength(const Stack *stack) {
return stack->length;
}
resizeStack 函数利用 realloc 函数根据传入的新的容量 newCapacity 计算分配内存的大小,重新设置栈容量。
/**
* @brief 调整动态栈结构内存大小
* @param Stack *stack 栈
* @param size_t newCapacity 新的容量
*/
void resizeStack(Stack *stack, size_t newCapacity) {
stack->data = (element_t*)realloc(stack->data, sizeof(element_t) * newCapacity);
stack->capacity = newCapacity;
}
pushStack 函数入栈,先判断容量是否足够,不足时调用 resizeStack 函数扩容,再从栈尾入栈。
/**
* @brief 入栈 (添加元素)
* @param Stack *stack
* @param element_t element 要添加的元素值
*/
void pushStack(Stack *stack, element_t element) {
// 判断容量是否足够
if (stack->length == stack->capacity) {
// 扩容
resizeStack(stack, stack->capacity * 2);
}
// 从栈的尾部入栈
stack->data[stack->length] = element;
// 长度自增
stack->length++;
}
popStack 函数出栈(删除元素),先将栈尾元素的地址(栈尾元素的下标是栈长度减 1)保存,再将栈长度减一。
/**
* @brief 出栈(删除元素)
* @param Stack *stack
* @param element_t *deleted_element 用于存储被删除的元素
* @return bool 1 表示成功 0 表示失败
*/
bool popStack(Stack *stack, element_t *deleted_element) {
// 判断是否为空栈
if (stack->length == 0) {
return false;
}
// 出栈
*deleted_element = stack->data[stack->length - 1];
// 长度自减
stack->length--;
return true;
}
freeStack 函数释放栈内存。
/**
* @brief 释放栈内存
* @param Stack *stack
*/
void freeStack(Stack *stack) {
free(stack->data);
stack->data = NULL;
stack->capacity = 0;
stack->length = 0;
}
队列 (Queue):也是操作受限的线性表,限制为仅允许在表的一端进行插入 (入队或进队),在表的另一端进行删除 (出队或离队) 操作。先进先出 (First In First Out,简称 FIFO)。队列中没有元素时,称为空队列。
相关概念:
队列的存储结构:顺序队列、链式队列。
顺序队列中存在'假溢出'现象:尽管队列中实际元素个数可能远远小于数组大小,但可能由于尾指针 rear 已超出向量空间的上界而不能做入队操作。为充分利用空间,克服上述'假溢出'现象,有两种方法。

定义队列中元素的类型,定义队列结构体,包含队列的内存地址、队列容量、队列长度、队首、队尾等参数。
// 定义队列中元素的类型
typedef int element_t;
// 定义结构体,描述队列
typedef struct {
element_t* data; // 内存地址
size_t capacity; // 队列容量
size_t length; // 队列长度
size_t front; // 队首 (指向下一个出队的元素)
size_t rear; // 队尾 (指向下一个入队的位置)
} Queue;
initQueue 函数利用 malloc 函数动态分配队列内存,初始化队列的容量、长度、队首和队尾。
/**
* @brief 初始化队列
* @param Queue *queue
* @param size_t initCapacity
*/
void initQueue(Queue *queue, size_t initCapacity) {
// 动态分配内存
queue->data = (element_t*)malloc(sizeof(element_t) * initCapacity);
// 其他成员赋值
queue->capacity = initCapacity;
queue->length = 0;
queue->front = 0;
queue->rear = 0;
}
getLength 函数直接访问队列长度参数,并返回队列的长度。
/**
* @brief 返回队列内元素个数
* @param const Queue *queue
* @return size_t 元素个数
*/
size_t getLength(const Queue *queue) {
return queue->length;
}
enqueueQueue 函数(队尾+1)% 队列容量这样能够让指向队尾的指针一直根据队列容量循环。/**
* @brief 入队,添加新元素
* @param Queue *queue
* @param element_t element
*/
void enqueueQueue(Queue *queue, element_t element) {
// 判断队列是否满,满队列不允许入队
if (queue->length == queue->capacity) {
printf("队列满,%d 入队失败!!!\n", element);
return;
}
// 入队
queue->data[queue->rear] = element;
// 计算下个入队的位置
queue->rear = (queue->rear + 1) % queue->capacity;
// 长度自增
queue->length++;
}
dequeueQueue 函数删除元素出队。
(队首+1)% 队列容量 这样使得指向队首的指针也一直根据队列容量循环。/**
* @brief 出队,删除元素
* @param Queue *queue
* @param element_t *deleted_element 用于存储被删除的元素
* @return bool 1 表示出队成功,0 表示出队失败
*/
bool dequeueQueue(Queue *queue, element_t *deleted_element) {
// 判断是否为空队列,空队列不允许出队
if (queue->length == 0) {
printf("空队列不允许出队\n");
return false;
}
// 出队的元素保存下来
*deleted_element = queue->data[queue->front];
// 计算下个出队的位置
queue->front = (queue->front + 1) % queue->capacity;
// 长度自减
queue->length--;
return true;
}
freeQueue 函数释放队列内存,利用 free 函数释放队列的值,再把队列的值赋 NULL、容量、长度、队首指针、队尾指针都赋 0。
/**
* @brief 释放队列内存
* @param Queue *queue
*/
void freeQueue(Queue *queue) {
free(queue->data);
// 重置成员值
queue->data = NULL;
queue->capacity = 0;
queue->length = 0;
queue->front = 0;
queue->rear = 0;
}
printfQueue 函数遍历队列,从队首处循环输出至队尾处。
/**
* @brief 遍历队列
* @param Queue *queue
*/
void printfQueue(Queue *queue) {
printf("capacity: %zu , length: %zu , front: %zu , rear: %zu \n", queue->capacity, queue->length, queue->front, queue->rear);
printf("元素:");
for (size_t i = 0; i < queue->length; i++) {
// 计算接下来要出队的位置
size_t index = (queue->front + i) % queue->capacity;
printf("%d ", queue->data[index]);
}
printf("\n\n");
}
int main() {
// 声明队列
Queue queue;
// 初始化队列
initQueue(&queue, 5);
// 入队
enqueueQueue(&queue, 100);
enqueueQueue(&queue, 200);
enqueueQueue(&queue, 300);
enqueueQueue(&queue, 400);
enqueueQueue(&queue, 500);
enqueueQueue(&queue, 600);
printfQueue(&queue);
// 出队
element_t delete_element;
dequeueQueue(&queue, &delete_element);
printfQueue(&queue);
dequeueQueue(&queue, &delete_element);
printfQueue(&queue);
// 释放
freeQueue(&queue);
return 0;
}

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online