队列12
像栈一样,队列也是一种线性表。它允许在表的一端插入数据,在另一端删除元素。插入元素的这一端称之为队尾。删除元素的这一端我们称之为队首。队列的结构如下图所示。
队列的特性:
• 在队尾插入元素,在队首删除元素。
• FIFO(先进先出),就向排队取票一样,先到先拿票
队列的相关概念
(1)队头( front() )与队尾( back() ): 允许元素插入的一端称为队尾,允许元素删除的一端称为队头。
(2)入队( push_back() ):队列的插入操作。
(3)出队( pop_front() ):队列的删除操作。
数组实现
先来看看数组实现的方法。栈使用top变量记录栈顶的位置,而队列则使用front和rear分别队列第一个元素和最后一个元素的位置。
#define SIZE 20
typedef struct queue
{
int arr[SIZE];
int front;
int rear;
} TyQueue;
入队、出队操作很简单。入队时,通过rear的位置判断队列是否已满。如果没有满,则将rear后移一位,将新元素放置在rear所在位置。出队时,也可以通过rear的位置判断队列是否为空。如果不空,则只需将front后移一位即可。 获取队头元素,判断队列不空,则只需返回front指向位置的元素即可。
这样就完成了队列的实现吗?你品,你细品,通过出队操作将数据弹出队列后,front之前的空间还能够再次得到吗?不能。所以使用普通数组实现队列,就再也不能使用front之前的空间了,这会导致大量空间丢失。
循环数组
为了解决这个问题,将普通数组换成循环数组。在循环数组中,末尾元素的下一个元素不是数组外,而是数组的头元素。这样就能够再次使用front之前的存储空间了。
但是如果这样排列就出现了新的问题,那就是无法判断队列是否满了,你品,你细品,那我画个图,如下:
所以循环队列中无法用头尾指针判断队列是否满了。
所以我们要加一个计数器,具体代码如下:
#include<iostream>
using namespace std;
#define SIZE 20
typedef struct queue
{
int arr[SIZE];
int front;
int rear;
int counter;
} TyQueue;
void InitQue(TyQueue *m_q)
{
m_q->front = 0;
m_q->rear = 0;
m_q->counter = 0;
}
void Push_back(TyQueue *m_q,int a)
{
//如果满了就不接受
if (m_q->counter>=SIZE)
{
printf("满了,不要再加了:%d\n",a);
return;
}
//没有满就插入尾巴
if (m_q->rear==SIZE)
{
m_q->rear = 0;
}
m_q->arr[(m_q->rear)++] = a;
m_q->counter++;
}
void popQue(TyQueue *m_q)
{
if (m_q->counter <=0)
{
return;
}
m_q->arr[m_q->front] = -1;
m_q->front++;
if (m_q->front >= SIZE)
{
m_q->front = 0;
}
m_q->counter--;
}
int top(TyQueue *m_q)
{
if (m_q->counter>0)
{
return m_q->arr[m_q->front];
}
else
{
return -1;
}
}
void main()
{
TyQueue m_que;
InitQue(&m_que);
for (int i = 0; i < SIZE+3;i++)
{
Push_back(&m_que, i+100);
}
popQue(&m_que);
popQue(&m_que);
popQue(&m_que);
popQue(&m_que);
Push_back(&m_que, 2020);
int ncount = m_que.counter;
printf("count:%d\n", ncount);
int a = 20;
for (int i = 0; i < ncount; i++)
{
printf("%d ", top(&m_que));
popQue(&m_que);
}
printf("\n");
system("pause");
}
结果:
以链表的实现就比较简单了和之前的栈实现过程类似,就不复述了。