像栈一样,队列(queue)也是一种线性表,它的特性是先进先出,插入在一端,删除在另一端。就像排队一样,刚来的人入队(push)要排在队尾(rear),每次出队(pop)的都是队首(front)的人。如图1,描述了一个队列模型。
队列(Queue)与栈一样,是一种线性存储结构,它具有如下特点:
- 队列中的数据元素遵循“先进先出”(First In First Out)的原则,简称FIFO结构。
- 在队尾添加元素,在队头删除元素。
1.2 队列的相关概念
队列的相关概念:
- 队头与队尾: 允许元素插入的一端称为队尾,允许元素删除的一端称为队头。
- 入队:队列的插入操作。
- 出队:队列的删除操作。
例如我们有一个存储整型元素的队列,我们依次入队:{1,2,3}
添加元素时,元素只能从队尾一端进入队列,也即是2只能跟在1后面,3只能跟在2后面。
如果队列中的元素要出队:
元素只能从队首出队列,出队列的顺序为:1、2、3,与入队时的顺序一致,这就是所谓的“先进先出”。
1.3 队列的操作
队列通常提供的操作:
- 入队: 通常命名为push()
- 出队: 通常命名为pop()
- 求队列中元素个数
- 判断队列是否为空
- 获取队首元素
1.4 队列的存储结构
队列与栈一样是一种线性结构,因此以常见的线性表如数组、链表作为底层的数据结构。
本文中,我们以数组、链表为底层数据结构构建队列。
2.基于数组的循环队列实现
以数组作为底层数据结构时,一般讲队列实现为循环队列。这是因为队列在顺序存储上的不足:每次从数组头部删除元素(出队)后,需要将头部以后的所有元素往前移动一个位置,这是一个时间复杂度为O(n)的操作:
可能有人说,把队首标志往后移动不就不用移动元素了吗?的确,但那样会造成数组空间的“流失”。
我们希望队列的插入与删除操作都是O(1)的时间复杂度,同时不会造成数组空间的浪费,我们应该使用循环队列。
所谓的循环队列,可以把数组看出一个首尾相连的圆环,删除元素时将队首标志往后移动,添加元素时若数组尾部已经没有空间,则考虑数组头部的空间是否空闲,如果是,则在数组头部进行插入。
C语言实现:数组实现的队列,并且只能存储int数据
C语言数组实现队列一(数组模拟):
#include <stdio.h> /** * C 语言: 数组实现的队列,只能存储int数据。 * * @author llz * @date 2018/7/02 */ // 保存数据的数组 int arr[500]; // 队列的实际大小 int count; // 将val添加到队列的末尾 void add(int val) { arr[count++] = val; } // 返回“队列开头元素” int front() { return arr[0]; } // 返回并删除“队列开头元素” int pop() { int i = 0;; int ret = arr[0]; count--; while (i++<count) arr[i-1] = arr[i]; return ret; } // 返回“队列”的大小 int size() { return count; } // 返回“队列”是否为空 int is_empty() { return count==0; } int main() { int tmp=0,i; // 打印队列 for(i=0;i<size();i++)printf("%d ",arr[i]); printf("\n"); // 将10, 20, 30 依次推入队列中 add(10); add(20); add(30); for(i=0;i<size();i++)printf("%d ",arr[i]); printf("\n"); // 将“队列开头的元素”赋值给tmp,并删除“该元素” tmp = pop(); printf("tmp=%d\n", tmp); // 只将“队列开头的元素”赋值给tmp,不删除该元素. tmp = front(); printf("tmp=%d\n", tmp); add(40); // 打印队列 printf("is_empty()=%d\n", is_empty()); printf("size()=%d\n", size()); while (!is_empty()) { printf("%d\n", pop()); } return 0; }
结果说明:该示例中的队列,是通过"数组"来实现的!
由于代码中已经给出了详细了注释,这里就不再对函数进行说明了。仅对主函数main的逻辑进行简单介绍。
(01) 在主函数main中,先将 "10, 20, 30"依次入队列。此时,队列的数据是: 10 --> 20 --> 30
(02) 接着通过pop()返回队首元素;pop()操作并不会改变队列中的数据。此时,队列的数据依然是: 10 --> 20 --> 30
(03) 接着通过front()返回并删除队首元素。front()操作之后,队列的数据是: 10 --> 30
(04) 接着通过add(40)将40入队列。add(40)操作之后,队列中的数据是: 10 --> 20 --> 40
C语言数组实现队列二(数组模拟+队头队尾指针+循环队列):
#include <stdio.h> /** * C 语言: 数组实现的队列,只能存储int数据。 * * @author llz * @date 2018/7/02 */ // 保存数据的数组 int arr[50]; // 队列的实际大小 int count; //队头和队尾下标 int front; int tail; int size() { return tail-front; } // 返回“队列”是否为空 int is_empty() { if(front==tail) return 1; return 0; } // 返回“队列”是否为满 int is_full() { if(front==(tail+1)%50) return 1; return 0; } // 将val添加到队列的末尾 void add(int val) { if(!is_full()) { arr[tail] = val; tail=(tail+1)%50; } else { printf("队列满\n"); } } // 返回“队列开头元素” int first() { return arr[front]; } // 返回并删除“队列开头元素” int pop() { int ret; if(!is_empty()) { ret=arr[front] ; front=(front+1)%50; } else { printf("队列空\n"); } return ret; } int main() { int tmp=0,i; //输出队列中的每个元素 for(i=0;i<size();i++)printf("%d ",arr[i]); printf("\n"); // 将10, 20, 30 依次推入队列中 add(10); add(20); add(30); for(i=0;i<size();i++)printf("%d ",arr[i]); printf("\n"); // 将“队列开头的元素”赋值给tmp,并删除“该元素” tmp = pop(); printf("tmp=%d\n", tmp); // 只将“队列开头的元素”赋值给tmp,不删除该元素. tmp = first(); printf("tmp=%d\n", tmp); add(40); // 打印队列 printf("is_empty()=%d\n", is_empty()); //printf("size()=%d\n", size()); while (!is_empty()) { printf("%d\n", pop()); } return 0; }