概述:
队列一种先进先出(FIFO)的数据结构,是一种只能在一端进行插入在另一端进行删除操作的特殊线性表。
队列的应用:
1、排队系统的实现
2、使用循环队列存储网络摄像头的数据帧(图像数据)
队列的具体实现:
1、顺序队列
2、链式队列
顺序队列:
顺序队列可以使用一维数组实现,在顺序队列中有两个指针,一指针front指向队列的队首(数组的第0个元素),一个指针rear指向队列的队尾。(此处可以为指针也可以是下表,便于移动位置即可)
不用进行删除操作,直接移动front指针即可。
1、顺序队列的描述
在这里我们使用数组和指针其实都可以的,但是因为数组是在栈上申请空间,当数据多的时候占用大量的栈空间。而指针是在堆上申请空间,有个好处就是可以手动申请,不用的时候可以释放掉。
#define QUEUE_INIT_LEN 100
#define QUEUEINCREMENT 10
typedef struct SqQueue {
ElemType *base; //存储堆上元素的地址空间的首地址
uint front; //队首指针
uint rear; //队尾指针
}SqQueue;
2、关于顺序队列操作的思考
如果对于顺序列队进行出队操作,队首指针怎么移动?
队首指针往后移动
对于顺序列队进行出队操作后被出队的元素占用的空间怎么处理?
只能暂时空着 ,等待下次循环使用
如何判断顺序列队是否为满,是否为空呢?
依据是front==rear,因此会有冲突
如何判断列队是否溢出呢?
当front为0,rear等于最大长度的时候为真溢出,
当front不为0,rear等于列队的最大长度的时候为假溢出。
因此,循环列队就显得尤为重要了
3、循环列队的概念
列队上的各个元素逻辑上形成一个圆环状。
如何判断循环列队是否为满?
- 将列队上的一个位置作为空闲位置
- 假设列队的长度为M,当(rear + 1)%M==font的时候认为列队为满(解释:rear为队尾,加上空闲的一个后除以总长度=队首)
4、循环顺序队列例程
#include <stdio.h>
#include "stdlib.h"
typedef int ElemType ;
typedef unsigned int uint;
#define QUEUE_INIT_LEN 10
#define QUEUEINCREMENT 10
#define TRUE 1
#define FALSE 0
typedef struct SqQueue {
ElemType *base; //存储堆上元素的地址空间的首地址,当数组来用
uint front; //指向队首
uint rear; //指向队尾
}SqQueue;
/* @brief 初始化一个顺序队列
* @param len 顺序队列的初始化长度(可以存储的元素的最大个数)
* @return 返回初始化的顺序队列 *
*/
SqQueue Sqqueue_init(uint len)
{
//先分配空间
SqQueue s;
s.base=(ElemType *)malloc(len*sizeof(ElemType));
//此时队内为空
s.front=s.rear=0;
//返回
return s;
}
/* @brief 获取顺序队列的长度
* @param s 顺序队列
* @return 顺序队列的长度 *
*/
int sqQueue_length(SqQueue s)
{
return s.rear-s.front;
}
/* @brief 判断顺序列队是否为空
* @param s 顺序队列
* @return 为空返回TRUE,不为空返回FALSE
*/
int is_empty(SqQueue s)
{
if(s.front==s.rear)
return TRUE;
else
FALSE;
}
/* @brief 判断顺序列队是否为满
* @param s 顺序队列
* @return 为满返回TRUE,不为满返回FALSE
*/
int is_full(SqQueue s)
{
if((s.rear+1)%QUEUE_INIT_LEN == s.front)
return TRUE;
else
return FALSE;
}
/* @brief 循环顺序列入队
* @param s 队列地址(在入队函数的时候需要去修改变量的值,所以要传指针)
* @param data 需要入队的元素
* @return 成功返回TRUE,失败返回FALSE
*/
int InSqQueue(SqQueue *s ,ElemType data)
{
if (NULL == s)//判断一下队列是否为空
FALSE;
if (is_full(*s) == TRUE) {
printf("[%s %d] queue is full....\n", __FUNCTION__, __LINE__);
}
s->base[s->rear] = data;
s->rear=(s->rear+1)%QUEUE_INIT_LEN;
return TRUE;
}
/* @brief 打印输出顺序列队的元素
* @param s 顺序队列
* @return 成功返回TRUE,失败返回FALSE
*/
int printf_sqQueue(SqQueue s)
{
if(is_empty(s) == TRUE)
{
printf("[%s %d] SqQueue is empty\n",__FUNCTION__ ,__LINE__);
return FALSE;
}
int i=s.front;
while(i!=s.rear)
{
printf("%d ",s.base[i]);
//指向下一个位置
i=(i+1)%QUEUE_INIT_LEN;
}
printf("\n");
return TRUE;
}
/* @brief 循环顺序列出队
* @param s 队列地址(在入队函数的时候需要去修改变量的值,所以要传指针)
* @param data 需要出队的元素
* @return 成功返回TRUE,失败返回FALSE
*/
int Out_SqQueue(SqQueue *s,ElemType *data)
{
if(is_empty(*s) == TRUE)
{
printf("[%s %d] SqQueue is empty\n",__FUNCTION__ ,__LINE__);
return FALSE;
}
*data = s->base[s->front];
s->front = (s->front+1)%QUEUE_INIT_LEN;//队首指针往后移动
return TRUE;
}
void main(void)
{
SqQueue s;
s=Sqqueue_init(QUEUE_INIT_LEN);
/*入队*/
int i;
for(i=0;i<5;i++)
InSqQueue(&s,i);
printf_sqQueue(s);
/*出队*/
ElemType data;
Out_SqQueue(&s,&data);
printf("OUT:%d\n",data);
printf_sqQueue(s);
}
链式队列:
链式队列可以理解为对单项链表的操作,入队就是单项链表的尾插法,出队就是需要销毁第一个数据结点(类似删除链表上第一个数据结点)。
1、链式循环队列例程
#include <stdio.h>
#include <stdlib.h>
#define FALSE 0
#define TRUE 1
/*
* 链式队列可以理解为对单项链表的操作,入队就是单项链表的尾插法,
* 出队则需要销毁第一个数据结点(类似删除链表上的第一个数据结点)
* *///没有必要完全和我的一样,只是一种指导思想,目的实现来实现FIFO(先进先出)即可
typedef int ElemType;
typedef unsigned int uint;
/*
* @brief 定义链表上的一个结点
* */
typedef struct LNode
{
ElemType data;
struct LNode *next;
}LNode;
/*
* @brief 定义一个结构体描述一个链式队列
* */
typedef struct LinkQueue
{
LNode *front;//LNode 类型的指针
LNode *rear;
}LinkQueue;
/*
* @brief 初始化一个链式队列
* @return 代表链式队列的结构体
* */
LinkQueue queue_init()
{
//创建一个头结点
LinkQueue L;
//当列队为空的时候,队首指针和队尾指针都为NULL
L.front=L.rear=NULL;
return L;
}
/*
* @brief 打印链式队列中的元素
* @param l 链式队列结构体
* @return
* */
void printf_queue(LinkQueue L)
{
//使用临时指针指向链表上的第一个数据结点(就是头结点的下一个结点)
LNode *tmp;
tmp = L.front;
while (tmp != NULL)
{
printf("%d ",tmp->data);
//让tmp指针指向下一个
tmp=tmp->next;
}
printf("\n");
}
/*
* @brief 入队
* @param L 链式队列的指针
* @param data 需要插入的元素
* @return 成功返回TRUE,失败返回FALSE
* */
int IN_queue(LinkQueue *L,ElemType data)
{
if(L==NULL)
return FALSE;
//先创建一个结点
LNode *p = (LNode *)malloc(sizeof(LNode));
p->next=NULL;
p->data=data;
//判断队列是否为空队列
if(L->front == NULL)
{
L->front = p;
L->rear = p;
return TRUE;
}
//如果队列不为空那么将rear指向新的结点
L->rear->next=p;
L->rear=p;
return TRUE;
}
/*
* @brief 出队
* @param L 链式队列的指针
* @param data 出队的元素
* @return 成功返回TRUE,失败返回FALSE
* */
int Out_queue(LinkQueue *L,ElemType *data)
{
if(L==NULL||data==NULL)
return FALSE;
if(NULL == L->front)
return FALSE;
//首先保存链式队列上的第一个结点的值
*data = L->front->data;
//使用临时指针执行队首
LNode *t=L->front;//将指向第一个结点的地址给t后,t就指向了该地址
//队首指针指向第一个结点(出队后原来列表上的第二个结点变成了新的队首结点)
L->front = L->front->next;
//释放原来队首结点的空间
free(t);
return TRUE;
}
int main() {
LinkQueue L;
L=queue_init();
//进队
int i;
for(i=0;i<5;i++)
IN_queue(&L,10+i);
printf_queue(L);
//出队
ElemType data;
Out_queue(&L,&data);
printf("Out:%d\n",data);
printf_queue(L);
return 0;
}