Files
2026-01-22 19:24:33 +08:00

193 lines
6.1 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "algo_queue.h"
/* 计算队列中第i个元素的地址 */
#define at(i) (((char *)(queue->base))+(i)*(queue->dsize))
/* 数据赋值将源地址s的数据复制到目标地址d */
#define assign(d, s) memcpy((d), (s), queue->dsize)
/* 获取队列中逻辑位置i的元素地址考虑循环队列 */
#define qa(i) at((queue->head + queue->capacity + (i)) % (queue->capacity))
/* 简易内存复制函数 */
static void* memcpy(void* dest, const void* src, int n)
{
char* dst = dest;
const char* s = src;
while (n--) *dst++ = *s++;
return dest;
}
/* 将队列中从index开始的size个元素向前移动一位 */
static void move_forward(queue queue, int index, int size)
{
for (int i = index; i < index + size; i++) assign(qa(i - 1), qa(i));
}
/* 将队列中从index开始的size个元素向后移动一位 */
static void move_backward(queue queue, int index, int size)
{
for (int i = index + size; i > index; i--) assign(qa(i), qa(i - 1));
}
/**
* \brief 标准入队操作(队尾添加)
* \param[in] queue: 队列对象指针
* \param[in] data: 待添加数据地址NULL表示仅移动指针
* \return 1=成功, 0=失败(队列无效或已满)
*
* \details 在队列尾部添加新元素。如果队列已满则操作失败。
* 当data不为NULL时将数据复制到队列存储区。
* 更新尾指针和队列大小。
*/
int queue_push(queue queue, void* data)
{
if (!queue) return 0; // 检查队列指针有效性
if (queue->size == queue->capacity) return 0; // 队列已满
if (data) assign(at(queue->tail), data); // 数据复制到队尾
queue->tail = (queue->tail + 1) % queue->capacity; // 更新尾指针(循环)
queue->size++; // 更新元素数量
return 1;
}
/**
* \brief 强制入队操作(覆盖式添加)
* \param[in] queue: 队列对象指针
* \param[in] data: 待添加数据地址NULL表示仅移动指针
* \return 1=成功, 0=失败(仅当队列无效时)
*
* \details 当队列未满时等同于queue_push()。
* 当队列已满时覆盖最旧数据(队头数据),
* 实现循环缓冲效果。
*/
int queue_push2(queue queue, void* data)
{
if (!queue) return 0;
if (queue->size < queue->capacity) return queue_push(queue, data); // 队列未满时调用标准入队
// 队列已满时覆盖队头数据(实现循环覆盖)
if (data) assign(at(queue->tail), data);
queue->tail = (queue->tail + 1) % queue->capacity;
queue->head = (queue->head + 1) % queue->capacity; // 头指针后移(覆盖最旧数据)
return 1;
}
/**
* \brief 出队操作(队头移除)
* \param[in] queue: 队列对象指针
* \param[out] data: 数据接收地址NULL表示丢弃数据
* \return 1=成功, 0=失败(队列无效或为空)
*
* \details 从队列头部移除元素。如果data不为NULL
* 将移除的元素复制到指定地址。
* 更新头指针和队列大小。
*/
int queue_pop(queue queue, void* data)
{
if (!queue) return 0;
if (queue->size == 0) return 0; // 队列为空
if (data) assign(data, at(queue->head)); // 复制队头数据到输出
queue->head = (queue->head + 1) % queue->capacity; // 更新头指针(循环)
queue->size--; // 更新元素数量
return 1;
}
/**
* \brief 指定位置插入元素
* \param[in] queue: 队列对象指针
* \param[in] index: 插入位置索引0=队头, size=队尾)
* \param[in] data: 待插入数据地址NULL表示仅空出位置
* \return 1=成功, 0=失败(索引越界或队列已满)
*
* \details 在指定位置插入新元素,后续元素后移。
* 自动选择最优移动方向(前移或后移元素)。
* 时间复杂度O(min(index, size-index))。
*/
int queue_insert(queue queue, int index, void* data)
{
if (!queue) return 0;
if (index < 0 || index > queue->size) return 0; // 索引越界检查
if (queue->size == queue->capacity) return 0; // 队列已满
// 根据插入位置选择最优移动方向
if (index <= queue->size / 2) {
// 向前移动前半部分元素
move_forward(queue, 0, index);
queue->head = (queue->head + queue->capacity - 1) % queue->capacity; // 头指针前移
} else {
// 向后移动后半部分元素
move_backward(queue, index, queue->size - index);
queue->tail = (queue->tail + 1) % queue->capacity; // 尾指针后移
}
// 在空出的位置插入数据
if (data) assign(at((queue->head + index) % (queue->capacity)), data);
queue->size++;
return 1;
}
/**
* \brief 指定位置删除元素
* \param[in] queue: 队列对象指针
* \param[in] index: 删除位置索引0=队头, size-1=队尾)
* \param[out] data: 数据接收地址NULL表示丢弃数据
* \return 1=成功, 0=失败(索引越界或队列为空)
*
* \details 删除指定位置元素,后续元素前移。
* 自动选择最优移动方向。
* 时间复杂度O(min(index, size-index))。
*/
int queue_erase(queue queue, int index, void* data)
{
if (!queue) return 0;
if (index < 0 || index >= queue->size) return 0; // 索引越界检查
if (queue->size == 0) return 0; // 队列为空
// 保存被删除元素(如果需要)
if (data) assign(data, at((queue->head + index) % (queue->capacity)));
// 根据删除位置选择最优移动方向
if (index <= queue->size / 2) {
// 向后移动前半部分元素
move_backward(queue, 0, index);
queue->head = (queue->head + 1) % queue->capacity; // 头指针后移
} else {
// 向前移动后半部分元素
move_forward(queue, index + 1, queue->size - index - 1);
queue->tail = (queue->tail + queue->capacity - 1) % queue->capacity; // 尾指针前移
}
queue->size--;
return 1;
}
/**
* \brief 清空队列
* \param[in] queue: 队列对象指针
*
* \details 重置队列状态(头尾指针归零,大小归零)。
* 不释放存储内存,保持队列容量不变。
*/
void queue_clear(queue queue)
{
if (!queue) return;
queue->tail = 0;
queue->head = 0;
queue->size = 0;
}
/**
* \brief 获取元素地址
* \param[in] queue: 队列对象指针
* \param[in] index: 元素索引0=队头, size-1=队尾)
* \return 元素地址失败返回NULL
*
* \details 获取队列中指定位置元素的直接指针。
* 可用于直接修改元素值(无需复制)。
* \warning 返回的指针在队列结构修改后可能失效。
*/
void* queue_data(queue queue, int index)
{
if (!queue) return 0;
if (index < 0 || index >= queue->size) return 0; // 索引越界检查
return (void*)at((queue->head + index) % (queue->capacity));
}