循环队列FIFO原理图解

1 循环队列FIFO介绍#

循环队列是把顺序队列首尾相连,把存储队列元素的表从逻辑上看成一个环,成为循环队列。

img

入队时尾指针向前追赶头指针;出队时头指针向前追赶尾指针。

1.1 循环队列结构#

1
2
3
4
5
#define FIFO_HEAD(name, type)                                          \
struct name { \
struct type *fifo; \
int front, tail, capacity; \
}
1
2
3
4
front表示首元素索引
tail表示最后一个元素索引
capacity表示队列的长度
struct type fifo表示该队列中的元素指针,可以指向任意结构体指针

举个例子:

1
2
3
4
5
6
7
8
9
10
11
struct person{
int age;
int id;
char name[20];
};
FIFO_HEAD(person_q, person*);
//等价于
struct person_q { \
struct person* *fifo; \
int front, tail, capacity; \
}

1.2 FIFO初始化#

分配一个连续的空间存储队列元素。用户自定义队列容量。

img

1
2
3
4
5
#define FIFO_INIT(head, _capacity) do {                                  \
(head)->fifo = malloc(sizeof(*(head)->fifo) * _capacity); \
(head)->front = (head)->tail = -1; \
(head)->capacity = _capacity; \
} while (0)

1.3 FIFO销毁#

1
2
3
4
5
6
#define FIFO_EXIT(head) do {                                                \
(head)->front = (head)->tail = -1; \
(head)->capacity = 0; \
if ((head)->fifo) \
free((head)->fifo); \
} while (0)

1.4 入队列#

入队列就是尾元素的索引++,也就是tail++,让新元素放进队列的尾部。

1
2
3
4
5
6
7
8
#define FIFO_PUSH(head, elm) do {                                      \
if (FIFO_EMPTY(head)) \
(head)->front = (head)->tail = 0; \
else \
(head)->tail = ((head)->tail == (head)->capacity - 1) \
? 0 : (head)->tail + 1; \
(head)->fifo[(head)->tail] = elm; \
} while (0)

如果队列是空的,则第一个元素入队列,front和tail索引都指向第一个元素,front = tail = 0;
img

其他情况入队,让tail++
img

img

img

1.5 出队列#

出队列就是让font对应的元素丢出去,font++。

1
2
3
4
5
6
7
8
#define FIFO_POP(head, pelm) do {                                      \
*(pelm) = (head)->fifo[(head)->front]; \
if ((head)->front == (head)->tail) \
(head)->front = (head)->tail = -1; \
else \
(head)->front = ((head)->front == (head)->capacity - 1)\
? 0 : (head)->front + 1; \
} while (0)

当front追上tail后,表示队列空了,重新设置起始点,需要将front = tail = -1 。

img

其他情况出队,丢出front元素,让front++

img

1.6 FIFO判空#

1
#define FIFO_EMPTY(head)    ((head)->front == -1)

①队列初始化时,队列是空的,会让front为-1
②出队列时,font++, 当font追上tail表示空了,则可以重新设置起始点,令front = tail = -1
综合①②所以可以用-1判断

1.6 FIFO判满#

1
#define FIFO_FULL(head)     (((head)->front == ((head)->tail + 1)%(head)->capacity))

①当front=0时,那么tail到达capacity-1表示FIFO full。

img

②否则,tail追上front后(front = tail + 1)表示FIFO full。

img

1.7 FIFO容量#

1
#define FIFO_CAPACITY(head) ((head)->capacity)

1.8 FIFO中有效元素个数#

1
2
#define FIFO_SIZE(head)     (FIFO_EMPTY(head) ? \
0 : ((((head)->tail + (head)->capacity - (head)->front) % (head)->capacity) + 1))

用tail - front就表示有效元素个数,不过由于循环FIFO,可能tail<front,这个时候就需要取余运算,如下图:

img

1.9 FIFO遍历#

1
2
3
4
#define FIFO_FOREACH(var, head, idx) \
  for (idx = (head)->front, var = (head)->fifo[idx]; \
    idx < (head)->front + FIFO_SIZE(head); \
    var = (head)->fifo[++idx % (head)->capacity])

1.10 队列元素获取#

1.10.1 第一个元素#

1
#define FIFO_GET_FRONT(head, pelm) (*(pelm) = (head)->fifo[(head)->front])

1.10.2 最后一个元素#

1
#define FIFO_GET_TAIL(head, pelm) (*(pelm) = (head)->fifo[(head)->tail])

2 测试用例#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#include "fifo.h"
#include <stdio.h>
struct person{
int age;
int id;
char name[20];
};

FIFO_HEAD(person_q, person*);
struct person_q person1_queue;
struct person_q person2_queue;

int main(void){
FIFO_INIT(&person1_queue, 1);
FIFO_INIT(&person2_queue, 5);

if (FIFO_CAPACITY(&person1_queue) != 1) {
printf( "FIFO_CAPACITY 1 NG.\n");
return -1;
}

if (FIFO_CAPACITY(&person2_queue) != 5) {
printf( "FIFO_CAPACITY 2 NG.\n");
return -1;
}

if (FIFO_SIZE(&person1_queue) != 0) {
printf( "FIFO_SIZE 1 NG.\n");
return -1;
}

if (FIFO_SIZE(&person2_queue) != 0) {
printf( "FIFO_SIZE 2 NG.\n");
return -1;
}

if (!FIFO_EMPTY(&person1_queue)) {
printf( "FIFO_EMPTY 1 NG.\n");
return -1;
}

if (!FIFO_EMPTY(&person2_queue)) {
printf( "FIFO_EMPTY 2 NG.\n");
return -1;
}

struct person *person_a = malloc(sizeof(*person_a));
person_a->age = 20;
person_a->id = 1001;
FIFO_PUSH(&person1_queue, person_a);//把person_a这个结构体指针元素丢进FIFO,
//后面对它pop出来又能拿到它,所以不用担心地址弄丢导致无法释放.

if (!FIFO_FULL(&person1_queue)) {
printf( "FIFO_FULL 1 NG.\n");
return -1;
}

person_a = malloc(sizeof(*person_a));
person_a->age = 30;
person_a->id = 1002;
FIFO_PUSH(&person2_queue, person_a);

if (FIFO_FULL(&person2_queue)) {
printf( "FIFO_FULL 2 NG.\n");
return -1;
}

if (FIFO_SIZE(&person1_queue) != 1) {
printf( "FIFO_SIZE 3 NG.\n");
return -1;
}

if (FIFO_SIZE(&person2_queue) != 1) {
printf( "FIFO_SIZE 4 NG.\n");
return -1;
}

FIFO_POP(&person1_queue, &person_a);
if (person_a->age != 20) {
printf( "FIFO_POP content NG.\n");
return -1;
}
free(person_a);

if (FIFO_SIZE(&person1_queue) != 0) {
printf( "FIFO_SIZE 5 NG.\n");
return -1;
}

person_a = malloc(sizeof(*person_a));
person_a->age = 40;
person_a->id = 1003;
FIFO_PUSH(&person2_queue, person_a);

FIFO_GET_FRONT(&person2_queue, &person_a);
if (person_a->age != 30) {
printf( "FIFO_GET_FRONT NG.\n");
return -1;
}

FIFO_GET_TAIL(&person2_queue, &person_a);
if (person_a->age != 40) {
printf( "FIFO_GET_TAIL NG.\n");
return -1;
}

FIFO_POP(&person2_queue, &person_a);
if (person_a->age != 30) {
printf( "FIFO_POP content NG.\n");
return -1;
}
free(person_a);

if (FIFO_SIZE(&person2_queue) != 1) {
printf( "FIFO_SIZE 6 NG.\n");
return -1;
}

FIFO_POP(&person2_queue, &person_a);
if (person_a->age != 40) {
printf( "FIFO_POP content NG.\n");
return -1;
}
free(person_a);

if (FIFO_SIZE(&person2_queue) != 0) {
printf( "FIFO_SIZE 7 NG.\n");
return -1;
}

struct person *person_arr[5];
int i=0;

while (!FIFO_FULL(&person2_queue)) {
person_arr[i] = malloc(sizeof(*person_arr[0]));
person_arr[i]->age = i;
person_arr[i]->id = 1000 + i;
FIFO_PUSH(&person2_queue, person_arr[i]);
i++;
}

while (!FIFO_EMPTY(&person2_queue) {
FIFO_POP(&person2_queue, &person_a);
printf( "age:%d, id:%d.\n", person_a->age, person_a->id);
free(person_a);
}

FIFO_EXIT(&person1_queue);
FIFO_EXIT(&person2_queue);
return 0;
}

结果如下:

img