队列
定义
队列(queue)是一种遵循先入后到规则的线性数据结构,将队列头部称为“队首”,尾部称为“队尾”,把元素加入队尾称为“入队”,删除队首元素称为“出队”。
队列实现
基于链表的实现
将链表的头节点和尾结点分别视为“队首”和“队尾”,规定队尾仅可添加节点,队首仅可删除节点。
/* 基于链表实现的队列 */
typedef struct
{ListNode *front, *rear;int queSize;
} LinkedListQueue;/* 构造函数 */
LinkedListQueue *newLinkedListQueue()
{LinkedListQueue *queue = (LinkedListQueue *)malloc(sizeof(LinkedListQueue));queue->front = NULL;queue->rear = NULL;queue->queSize = 0;return queue;
}/* 析构函数 */
void delLinkedListQueue(LinkedListQueue *queue)
{// 释放所有节点while (queue->front != NULL){ListNode *tmp = queue->front;queue->front = queue->front->next;free(tmp);}// 释放 queue 结构体free(queue);
}/* 获取队列的长度 */
int size(LinkedListQueue *queue)
{return queue->queSize;
}/* 判断队列是否为空 */
bool empty(LinkedListQueue *queue)
{return (size(queue) == 0);
}/* 入队 */
void push(LinkedListQueue *queue, int num)
{// 尾节点处添加 nodeListNode *node = newListNode(num);// 如果队列为空,则令头、尾节点都指向该节点if (queue->front == NULL){queue->front = node;queue->rear = node;}// 如果队列不为空,则将该节点添加到尾节点后else{queue->rear->next = node;queue->rear = node;}queue->queSize++;
}/* 访问队首元素 */
int peek(LinkedListQueue *queue)
{assert(size(queue) && queue->front); // 队列为空时,不能访问队首元素,assert是宏,用于断言,断言失败会终止程序return queue->front->val;
}/* 出队 */
int pop(LinkedListQueue *queue)
{int num = peek(queue);ListNode *tmp = queue->front;queue->front = queue->front->next;free(tmp);queue->queSize--;return num;
}/* 打印队列 */
void printLinkedListQueue(LinkedListQueue *queue)
{int *arr = malloc(sizeof(int) * queue->queSize);// 拷贝链表中的数据到数组int i;ListNode *node;for (i = 0, node = queue->front; i < queue->queSize; i++){arr[i] = node->val;node = node->next;}printArray(arr, queue->queSize);free(arr);
}
基于数组实现
在数组中删除首元素的时间复杂度为O(N),导致出队效率低,可以用一个巧妙方法避免这个问题。
使用一个变量front
指向队首元素的索引,并维护一个变量size
用于记录队列长度,则定义rear=front+size
,计算出rear
指向队尾元素的下个位置,则数组中包含元素的有效区间为[front,rear-1]
,这时,出入队操作只需进行一次操作,时间复杂度均为O(1).
有一个问题:在不断入队和出队的过程中,front和rear都在向右移动,当他们到达数组尾部就无法继续移动了,为了解决这个问题,可以将数组视为首尾相接的“环形数组”,
实现思路:我们需要将front或rear再越过数组尾部时,直接回到数组头部继续遍历,这种周期性规律可以通过“取余操作”来实现
/* 基于环形数组实现的队列 */
typedef struct
{int *nums; // 用于存储队列元素的数组int front; // 队首指针,指向队首元素int queSize; // 队列长度,即队列中元素的个数int queCapacity; // 队列容量,即数组长度
} ArrayQueue;/* 构造函数 */
ArrayQueue *newArrayQueue(int capacity)
{ArrayQueue *queue = (ArrayQueue *)malloc(sizeof(ArrayQueue));// 初始化数组queue->queCapacity = capacity;queue->nums = (int *)malloc(sizeof(int) * queue->queCapacity);queue->front = queue->queSize = 0;return queue;
}/* 析构函数 */
void delArrayQueue(ArrayQueue *queue)
{free(queue->nums);free(queue);
}/* 获取队列的容量 */
int capacity(ArrayQueue *queue)
{return queue->queCapacity;
}/* 获取队列的长度 */
int size(ArrayQueue *queue)
{return queue->queSize;
}/* 判断队列是否为空 */
bool empty(ArrayQueue *queue)
{return queue->queSize == 0;
}/* 访问队首元素 */
int peek(ArrayQueue *queue)
{assert(size(queue) != 0);return queue->nums[queue->front];
}/* 入队 */
void push(ArrayQueue *queue, int num)
{if (size(queue) == capacity(queue)){printf("队列已满\r\n");return;}// 计算队尾指针,指向队尾索引 + 1// 通过取余操作实现 rear 越过数组尾部后回到头部int rear = (queue->front + queue->queSize) % queue->queCapacity;// 将 num 添加至队尾queue->nums[rear] = num;queue->queSize++;
}/* 出队 */
int pop(ArrayQueue *queue)
{int num = peek(queue);// 队首指针向后移动一位,若越过尾部,则返回到数组头部queue->front = (queue->front + 1) % queue->queCapacity;queue->queSize--;return num;
}
典型应用
- 淘宝订单
- 代办事项:先来后到功能