队列
队列,简称队,它是一种操作受限的线性表,其限制在表的一端进行插入,另一端进行删除。可进行插入的一端称为队尾(rear),可进行删除的一端称为队头(front)。向队中插入元素叫入队,新元素进入之后就成为新的队尾元素。从队中删除元素叫出队,元素出队后,其后继结点元素就成为新的队头元素。
队列可以用数组或者链表的结构实现,但是用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。
代码实现
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int DataType;
typedef struct Node
{
struct Node* _next;
DataType _data;
}Node;
typedef struct Queue
{
Node* _front;
Node* _rear;
size_t _size;
}Queue;
void Init(Queue* q)
{
q->_front = q->_rear = NULL;
q->_size = 0;
}
Node* CreatNode(DataType val)
{
Node* node = (Node*)malloc(sizeof(Node));
node->_next = NULL;
node->_data = val;
return node;
}
void Push(Queue* q, DataType val)
{
Node* node = CreatNode(val);
if (q->_front == NULL)
{
q->_front = q->_rear = node;
}
else
{
q->_rear->_next = node;
q->_rear = node;
}
++q->_size;
}
void Pop(Queue* q)
{
assert(q->_front != NULL);
Node* next = q->_front->_next;
free(q->_front);
q->_front = next;
if (q->_front == NULL)
{
q->_rear = NULL;
}
--q->_size;
}
DataType Front(Queue* q)
{
assert(q->_front != NULL);
return q->_front->_data;
}
DataType Back(Queue* q)
{
assert(q->_front != NULL);
return q->_rear->_data;
}
int Empty(Queue* q)
{
if (q->_size == 0)
{
return 1;
}
return 0;
}
size_t Size(Queue* q)
{
return q->_size;
}
void Destroy(Queue* q)
{
if (q->_front)
{
Node* cur = q->_front;
while (cur)
{
Node* next = cur->_next;
free(cur);
cur = next;
}
q->_front = q->_rear = NULL;
q->_size = 0;
}
}
void Print(Queue* q)
{
Node* cur = q->_front;
while (cur)
{
printf("%d ", cur->_data);
cur = cur->_next;
}
printf("\n");
}
测试用例
void Test1()
{
Queue q;
Init(&q);
Push(&q, 1);
Push(&q, 2);
Push(&q, 3);
Push(&q, 4);
Print(&q);
Pop(&q);
Pop(&q);
Pop(&q);
Pop(&q);
Print(&q);
Push(&q, 10);
Push(&q, 20);
Push(&q, 30);
Push(&q, 40);
Print(&q);
Destroy(&q);
}
void Test2()
{
Queue q;
Init(&q);
Push(&q, 1);
Push(&q, 2);
Push(&q, 3);
Push(&q, 4);
Print(&q);
while (!Empty(&q))
{
printf("%d ", Front(&q));
Pop(&q);
}
printf("\n");
Destroy(&q);
}
环形队列
在实际使用中还有一种特殊的队列:循环队列。如操作系统课程讲解生产者消费者模型时就可以使用使用循环队列。环形队列可以使用数组实现,也可以使用循环链表实现。
循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。
leetcode原题:设计循环队列
- MyCircularQueue(k): 构造器,设置队列长度为 k 。
- Front: 从队首获取元素。如果队列为空,返回 -1 。
- Rear: 获取队尾元素。如果队列为空,返回 -1 。
- enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
- deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
- isEmpty(): 检查循环队列是否为空。
- isFull(): 检查循环队列是否已满。
代码实现
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct MyCircularQueue
{
int* _arr;
int _front;
int _rear;
int _capacity;
} MyCircularQueue;
bool myCircularQueueIsEmpty(MyCircularQueue* obj);
bool myCircularQueueIsFull(MyCircularQueue* obj);
//构造器,设置队列长度为 k
MyCircularQueue* myCircularQueueCreate(int k)
{
MyCircularQueue* cq = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
cq->_arr = (int*)malloc(sizeof(int) * (k + 1));
cq->_capacity = k + 1;
cq->_front = cq->_rear = 0;
return cq;
}
//向循环队列插入一个元素, 如果成功插入则返回真
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value)
{
if (myCircularQueueIsFull(obj))
{
return false;
}
obj->_arr[obj->_rear] = value;
obj->_rear = (obj->_rear + 1) % obj->_capacity;
return true;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj)
{
if (myCircularQueueIsEmpty(obj))
{
return false;
}
obj->_front = (obj->_front + 1) % obj->_capacity;
return true;
}
int myCircularQueueFront(MyCircularQueue* obj)
{
if (myCircularQueueIsEmpty(obj))
{
return -1;
}
return obj->_arr[obj->_front];
}
int myCircularQueueRear(MyCircularQueue* obj)
{
if (myCircularQueueIsEmpty(obj))
{
return -1;
}
return obj->_arr[(obj->_rear - 1 + obj->_capacity) % obj->_capacity];;
}
//检查循环队列是否为空
bool myCircularQueueIsEmpty(MyCircularQueue* obj)
{
return obj->_front == obj->_rear;
}
//检查循环队列是否已满
bool myCircularQueueIsFull(MyCircularQueue* obj)
{
return (obj->_rear + 1) % obj->_capacity == obj->_front;
}
void myCircularQueueFree(MyCircularQueue* obj)
{
obj->_front = obj->_rear = 0;
free(obj);
}