队列
1 概念
队列是一种先进先出(FIFO,First-In-First-Out)或后进后出(LILO,Last-In-Last-Out)的线性表,通常用链表或者数组来实现。
队列只能在队尾插入元素(入队),只能在队首删除元素(出队)。
2 基本操作
2.1 结构定义
实现队列需要引入两个变量,head用于标记队首元素的位置,tail用来标记队尾元素的位置;
// 普通队列的结构定义
typedef struct Queue {
int *data;
int head, tail, length;
} Queue;
// 循环队列的结构定义(顺序表)
typedef struct Queue {
int *data;
int head, tail, length;
int count; // 循环队列中元素的个数
} Queue;
// 链队列的结构定义
typedef struct Node {
int data;
struct Node *next;
} Node;
typedef struct Queue {
Node head, *tail;
int length;
} Queue;
2.2 出队(head后移)
2.3 入队(tail后移)
3 普通队列的“假溢出”问题
对于普通队列而言,一旦队列出队发生,这就意味着有了空闲的空间,然而这些空闲空间并不会被使用。当多次向队列执行入队操作,直到tail标记指向了队列容量的上限时,就有出现“假溢出”现象。如何避免“假溢出”呢?使用循环队列,通过累计队列元素的个数来判断队列的容量上限,而不是简单地通过tail标记做判断;或者通过链队列来实现。
4 代码演示
4.1 循环队列(入队+扩容、出队)
// 结构定义
typedef struct Queue {
int *data;
int head, tail, length;
int count;
} Queue;
// 创建队列
Queue *init(int n) {
Queue *q = (Queue *)malloc(sizeof(Queue));
q->data = (int *)malloc(sizeof(int) * n);
q->length = n;
q->head = q->tail = 0;
q->count = 0;
return q;
}
// 清空队列
void clear(Queue *q) {
if (q == NULL) return ;
free(q->data);
free(q);
return ;
}
// 队列判空操作
int empty(Queue *q) {
return q->count == 0;
}
// 获取队首元素
int front(Queue *q) {
return q->data[q->head];
}
// 入队
int push(Queue *q, int val) {
if (q == NULL) return 0;
// 队列已满,给队列扩容
if (q->count == q->length) {
if (!expand(q)) {
printf(RED("fail to expand!\n"));
return 0;
}
printf(GREEN("success to expand! the new size = %d\n"), q->length);
}
q->data[q->tail++] = val;
if (q->tail == q->length) q->tail = 0; // 循环队列首尾位置转换
q->count += 1;
return 1;
}
// 出队
int pop(Queue *q) {
if (q == NULL) return 0;
if (empty(q)) return 0;
q->head += 1;
if (q->head == q->length) q->head = 0; // 循环队列首尾位置转换
q->count -= 1;
return 1;
}
// 遍历
void output(Queue *q) {
printf("Queue(%d) : [", q->count);
for (int i = q->head, j = 0; j < q->count; j++) {
j && printf(", ");
printf("%d", q->data[(i + j) % q->length]);
}
printf("]\n");
return ;
}
// 扩容
int expand(Queue *q) {
int extr_size = q->length;
int *temp;
while (extr_size) {
temp = (int *)malloc(sizeof(int) * (q->length + extr_size));
if (temp != NULL) break;
extr_size >> 1;
}
if (temp == NULL) return 0; // 扩容失败(没有足够的内存)
for (int i = q->head, j = 0; j < q->count; j++) {
temp[j] = q->data[(i + j) % q->length];
}
free(q->data);
q->data = temp;
q->head = 0, q->tail = q->count;
q->length += extr_size;
return 1;
}
4.2 链队列(入队、出队)
// 链队列的结构定义
typedef struct Node {
int data;
struct Node *next;
} Node;
typedef struct Queue {
Node head, *tail;
int length;
} Queue;
Node *getNewNode(int val) {
Node *p = (Node *)malloc(sizeof(Node));
p->data = val;
p->next = NULL;
return p;
}
Queue *init_queue() {
Queue *q = (Queue *)malloc(sizeof(Queue));
q->head.next = NULL; // q->head.next 指向队首元素的地址
q->tail = &(q->head); // q->tail指向队首元素的虚拟地址
q->length = 0;
return q;
}
int empty(Queue *q) {
return q->length == 0;
}
int front(Queue *q) {
return q->head.next->data;
}
int push(Queue *q, int val) {
if (q == NULL) return 0;
Node *temp = getNewNode(val);
q->tail->next = temp; // 入队操作
q->tail = temp; // 指针后移
q->length += 1;
return 1;
}
int pop(Queue *q) {
if (q == NULL) return 0;
if (empty(q)) return 0;
Node *temp = q->head.next;
q->head.next = temp->next; // 指针后移
clear_node(temp); // 出队操作
q->length -= 1;
if (q->length == 0) q->tail = &(q->head); // 队列为空时,需要将q->tail指向队首元素的虚拟地址
return 1;
}
void clear_node(Node *node) {
if (node == NULL) return ;
free(node);
return ;
}
void clear(Queue *q) {
if (q == NULL) return ;
Node *p = q->head.next, *temp;
while (p != NULL) {
temp = p->next;
clear_node(p);
p = temp;
}
free(q);
return ;
}
void output(Queue *q) {
if (q == NULL) return ;
printf("Queue(%d) : [", q->length);
for (Node *p = q->head.next; p != NULL; p = p->next) {
p != q->head.next && printf(", ");
printf("%d", p->data);
}
printf("]\n");
return ;
}