循环队列(C数组实现)
总纲
- 队列的声明
- 队列的初始化
- 判断队列是否为空
- 入队
- 出队
- 获取队首元素
- 获取队尾元素
- 获取队列大小
- 打印队列
- 队列的销毁
注意: 这里的队列是循环的,如果满的话再加入元素会把前面的元素挤出去,也就是可以一直加入元素
一. 队列的声明
因为元素类型未知,所以用变量代替
一个队列结构体要具有队列容量,大小,数组的地址,首尾的索引
typedef int type;
typedef struct Cursionque
{
type* arr;
int front;
int rear;
int capacity;
int size;
}queue;
二. 队列的初始化
首先想一想队列初始的状态是什么
- 容量一定,大小为0
- 这里的队列是循环队列,更像是一个圈。所以front和rear都置为-1
void init(queue* que, int cap)
{
type* tmp = (type*)malloc(sizeof(type) * cap);
if (tmp == NULL)
{
printf("MallocError\n");
return;
}
else
{
que->arr = tmp;
que->capacity = cap;
que->front = que->rear = -1;
que->size = 0;
}
}
三. 判读队列是否为空
首位元素索引最好都判断一下
// 判断队列是否为空
bool ifEmpty(queue* que)
{
return (que->front == -1) && (que->rear == -1);
}
四. 入队
首先要判断队列是否为空
另外一个要注意的点是front的变化,因为是循环队列,所以front的变化也要是循环的,所以要模上capacity,这样的话当front到capacity-1(数据这时候已经满了)时,再次入队front就变成0了
// 入队
void Enque(queue* que, type x)
{
if (isEmpty(que))
{
que->rear++;
que->front++;
}
else
{
que->front = (que->front + 1 ) % (que->capacity);
}
que->arr[que->front] = x;
if (que->size != que->capacity)
{
que->size++;
}
}
五. 出队
出队也要判断为空的情况,也要判断元素个数是不是为1的情况,因为这时候要同时移动front和rear
因为是循环队列,rear也要模上capacity
// 出队
void Deque(queue* que)
{
if (isEmpty(que))
{
printf("AlreadyEmpty\n");
return;
}
else if (que->front == que->rear)
{
que->front = -1, que->rear = -1;
}
else
{
que->rear = (que->rear + 1) % (que->capacity);
}
que->size--;
}
六. 获取队首元素
这个倒没什么需要特别注意的
// 获取队首元素
type Front(queue* que)
{
if (isEmpty(que))
{
exit(1);
}
else
{
return que->arr[que->front];
}
}
七. 获取队尾元素
这个也没什么特别需要注意的
// 获取队尾元素
type Rear(queue* que)
{
if (isEmpty(que))
{
exit(1);
}
else
{
return que->arr[que->rear];
}
}
八. 获取队列大小
// 获取队列大小
int sz(queue* que)
{
return que->size;
}
九. 打印队列
首先需要判空
if (isEmpty(que))
{
printf("AlreadyEmpty\n");
return;
}
这里是循环队列,由于之前对front和rear的设置所以可能出现front小于rear的情况,而且我们是要从队首打印到队尾,这时可以利用size作为循环的条件
// 打印队列
void print(queue* que)
{
// 由于是循环队列所以可能出现front小于rear的情况, 所以要特判一下
if (isEmpty(que))
{
printf("AlreadyEmpty\n");
return;
}
// 注意这里是从队首打印到队尾
// 由于是循环队列所以可能出现front小于rear的情况, 所以要特判一下
for (int i = 0; i < que->size; i++)
{
int j = (que->front - i + que->capacity) % (que->capacity);
printf("%d ", que->arr[j]);
}
}
有size个元素,就打印size次就好了
如果front大于rear,就减去i就好了
如果front小于rear呢,减去i可能会变成负数怎么办(如果C语言支持负数索引这个就没什么问题了) ————可以加上capacity再模上capacity(这样就是物理上从数组头部的位置移动到了尾部位置,逻辑上仍然是一个循环数组)
十. 队列的销毁
释放申请的空间,指针置空,全部置为初始状态
// 队列的销毁
void destroy(queue* que)
{
free(que->arr);
que->arr = NULL;
que->capacity = 0;
que->size = 0;
que->rear = -1;
que->front = -1;
}
测试
int main()
{
queue test;
init(&test, 5);
for (int i = 0; i < 5; i++)
{
Enque(&test, i + 1);
printf("sz = %d, cap = %d", test.size, test.capacity);
printf("\n");
print(&test);
printf("\n");
printf("front = %d, rear = %d\n", test.front, test.rear);
}
for (int i = 0; i < 3; i++)
{
Deque(&test);
printf("sz = %d, cap = %d", test.size, test.capacity);
printf("\n");
print(&test);
printf("\n");
printf("front = %d, rear = %d\n", test.front, test.rear);
}
for (int i = 0; i < 8; i++)
{
Enque(&test, i * 2);
printf("sz = %d, cap = %d", test.size, test.capacity);
printf("\n");
print(&test);
printf("\n");
printf("front = %d, rear = %d\n", test.front, test.rear);
}
return 0;
}