队列与栈类似,也是一种操作受限的线性表。只支持FIFO先进先出。
一般具有的操作:
- 初始化
- 入队
- 出队
- 判空
- 判满
- 析构
队列在处理队头队尾指针时候也有两种处理方式,将使得代码以及判空判满操作有区别
- 队尾指针指向将要插入位置的前一个
- 队尾指针指向将要插入位置
队列的问题相较于栈多一点。
-
由于队列是一种先进先出的结构,如果在内存空间中不断进行入队出队将会使得内存空间地址不断增长。
考虑解决办法:循环队列,将队头队尾指针通过模运算映射到开辟的地址空间范围内。 -
以队尾指针指向将要插入位置为例子,在进行判断空队列时候通常以front == rear进行判断,但是在循环队列队满的时候,此时front也会等于rear。
考虑的解决办法主要有三种
- 在进行队尾插入的时候,留一个空位置,此时判满的条件为(rear + 1 %)MaxSize ==front,而不再是front==rear。缺点是有一个空间将被浪费,得不到利用。
- 设置一个size,表示当前队列中含有的元素个数,size = MaxSize 即为队满 size = 0即为队空
- 设置一个标志位tag,用于存放最近对于队列的操作,因为队满只会在入队的操作下满足,队空只会在出队的操作下满足。所以结合tag与front = rear联合判断就可以判断当前队列为空、满还是非空非满
队列求长度,背下来 :(rear - front + MaxSize) % maxSize
以顺序表实现的队列,以队尾指针指向将要插入的位置为例子
#define MaxSize 10
typedef struct {
ElemType data[MaxSize];
int front, rear;
int tag, size;
//size用于解决判满
//假定tag为1表示最近一次为入队,为0表示最近一次为出队
}Queue;
void InitQueue(Queue &q){
q.front = q.rear = 0;
//q.size = 0;
//q.tag = 0;
}
bool isEmpty(Queue q){
return q.front == q.rear;
return size == 0;
return q.front == q.rear && tag == 0;
}
bool isFull(Queue q){
return (q.rear + 1) % MaxSize == q.front;
return size == MaxSize;
return q.front == q.rear && tag == 1;
}
bool push(Queue &q, ElemType e){
if (isFull(q)){
return false;
}
q.data[rear] = e;
rear = (rear + 1) % MaxSize;
//size ++;
//tag = 1;
return true;
}
bool pop(Queue &q, ElemType &e){
if(isEmpty(q))
return false;
e = q.data[front];
front = (front + 1) % maxSize;
//tag = 0;
//size —-;
return true;
}
bool getFront(Queue q, ElemType &e){
if (isEmpty(q))
return false;
e = q.data[front];
return true;
}
int getLength(Queue q){
return (q.rear - q.front + MaxSize) % MaxSize;
}
链式存储,以带头结点为例子
注意,链式存储一般不会满,因为只要内存空间有剩余,都能继续分配结点,所以出现满的概率很低
typedef struct Node{
ElemType data;
Node *next;
}QNode;
typedef struct {
QNode * front, rear;
}Queue;
void InitQueue(Queue &q){
q.front = (QNode)malloc(sizeof(QNode));
q.rear = front;
}
void isEmpty(Queue q){
return front == rear;
}
void push(Queue &q, ElemType e){
QNode * p = (QNode *)malloc(sizeof(QNode));
p->next = q.rear->next;
p->data = e;
q.rear->next = p;
return true;
}
bool pop(Queue &q, ElemType &e){
if (isEmpty(q))
return false;
QNode * p = q.front->next;
e = p->data;
q.front->next = p->next;
free(p);
return true;
}
bool getFront(Queue q, ElemType &e){
if (isEmpty(q))
return false;
e = q.front->next->data;
return true;
}
双端队列
双端队列一般指的是可以在两头都可以进行插入与删除操作。
受输入限制的双端队列,指的是在头部只能进行删除而不能进行插入的双端队列。
受输出限制的双端队列,指的是在头部只能进行插入而不能进行删除的双端队列。
一般考判断输出序列是否合法:
- 只要栈能够实现,双端队列一定能实现
- 如果是受输入限制的双端队列,先看第一个输出,确定输入,然后拼凑 输出
- 如果是受输出限制的双端队列,先预测输出方式,凑输入