文章目录
1.队列
**队列:**先进先出的线性表,受到限制的,一端进行入队,一端进行出队。
入队的一端叫做队尾,出队的一端叫做队头,队列中没有元素的话,叫做空队。
2.循环队列
循环队列就是将队列存储空间的最后一个位置绕到第一个位置,形成逻辑上的环状空间,供队列循环使用。在循环队列结构中,当存储空间的最后一个位置已被使用而再要进入队运算时,只需要存储空间的第一个位置空闲,便可将元素加入到第一个位置,即将存储空间的第一个位置作为队尾。循环队列可以更简单防止伪溢出的发生,但队列大小是固定的。
在循环队列中,当队列为空时,有front=rear,而当所有队列空间全占满时,也有front=rear。为了区别这两种情况,规定循环队列最多只能有 MaxSize-1个队列元素,当循环队列中只剩下一个空存储单元时,队列就已经满了。因此,队列判空的条件是front=rear,而队列判满的条件是front=(rear+1)%MaxSize。
循环队列有三个难点:
第一个:如何保证队列的入队和出队时间复杂度都为O(1)
第二个:如何保证判空和判满的条件不冲突?
第三个:如何求循环队列有效长度?
我们可以模拟火车上售货模式,也就是说,出队和入队的时候,数据不需要挪动,直接挪动队头指针和队尾指针即可
现在这种模式确实可以让队列的入队和出队时间复杂度都为O(1)
但是这种设计,会引发第二个问题?
判空和判满,会发生冲突,因为判断条件都是 队头==队尾
解决方案:
我们可以臆想成一个环形:实际上还是一个数组
判空和判满,会发生冲突,因为判断条件都一样
有两种方法可以解决:
第一种: 额外申请一个变量count,保存有效值个数
判空:队头 == 队尾 且 count== 0
判满:队头 == 队尾 且 count !=0
**第二种:**将循环队列中队尾处,浪费一个空间不用,这个浪费的空间不再保存有效值,而是作为一个标记位
判空: 队头==队尾
判满: 队尾向后走一步 遇到了队头,则判满
动态位是动态变化的
有效长度为 6 ,怎么得到时间复杂度为O(1)?
如果:队尾下标>队头下标 rear - front
如果:队尾<队头 rear - front + MAX_SIZE
那么 我们有了这种想法以后,就无需判断队尾和队头谁更大,直接使用下面的这个公式即可:
循环队列求长度公式 : ( rear - front + MAX_SIZE ) % MAX_SIZE
+MAX_SIZE : 防止 rear-front 出现负数
%MAX_SIZE:防止rear-front不是负数,导致+MAX_SIZE加多了****
3.循环队列头文件与函数声明
#pragma once
//循环队列的结构体设计:
typedef int ELEM_TYPE;
#define MAX_SIZE 100
typedef struct Queue
{
ELEM_TYPE *base;//一个数组(用于保存数据)
int front;//队头指针(保存数组下标,INT)
int rear;//队尾指针(保存数组下标,INT)
}Queue, *PQueue;
//增删改查
//初始化
void Init_queue(struct Queue * qu);
//入队
bool Push(PQueue qu, ELEM_TYPE val);
//出队(既要返回出队的值,还有返回操作是否成功,所以还需要一个输出参数)
bool Pop(PQueue qu, ELEM_TYPE *rtval);
//获取队头值
bool Top(PQueue qu, ELEM_TYPE *rtval);
//判空
bool IsEmpty(PQueue qu);
//判满
bool IsFull(PQueue qu);
//查找
int Search(PQueue qu, ELEM_TYPE val);
//获取队列有效长度
int Get_length(PQueue qu);
//清空
void Clear(PQueue qu);
//销毁
void Destroy(PQueue qu);
//打印
void Show(PQueue qu);
4.函数
4.1初始化
从堆区申请空间,初始化队头与队尾指针
//初始化
void Init_queue(struct Queue * qu)
{
//assert
qu->base = (ELEM_TYPE *)malloc(MAX_SIZE * sizeof(ELEM_TYPE));
assert(qu->base != NULL);
qu->front = qu->rear = 0;
}
4.2入队
通过队头指针和队尾指针入队
//入队
bool Push(PQueue qu, ELEM_TYPE val)
{
//assert
if(IsFull(qu))
{
return false;
}
qu->base[qu->rear] = val;
//qu->rear++;//error
qu->rear = (qu->rear+1)%MAX_SIZE;
return true;
}
4.3出队
//出队(既要返回出队的值,还有返回操作是否成功,所以还需要一个输出参数)
bool Pop(PQueue qu, ELEM_TYPE *rtval)
{
//assert qu rtval
if(IsEmpty(qu))
{
return false;
}
*rtval = qu->base[qu->front];
//qu->front++;//error
qu->front = (qu->front+1)%MAX_SIZE;
return true;
}
4.4获取队头值
//获取队头值
bool Top(PQueue qu, ELEM_TYPE *rtval)
{
//assert qu rtval
if(IsEmpty(qu))
{
return false;
}
*rtval = qu->base[qu->front];
return true;
}
4.5判空与判满
//判空
bool IsEmpty(PQueue qu)
{
return qu->front == qu->rear;
}
//判满 如果队尾向后再走一步,遇到了队头,此时则认为队列已经满了
bool IsFull(PQueue qu)
{
//assert
return (qu->rear+1)%MAX_SIZE == qu->front;
}
4.6查找
//查找
int Search(PQueue qu, ELEM_TYPE val)
{
//assert
//实际上是一个数组,所以将其遍历一般,即可
for(int i=qu->front; i!=qu->rear; i=(i+1)%MAX_SIZE)
{
if(val == qu->base[i])
{
return i;
}
}
return -1;
}
4.7获取队列有效长度
//获取队列有效长度
int Get_length(PQueue qu)
{
return (qu->rear - qu->front + MAX_SIZE) % MAX_SIZE;
}
4.8清空与销毁
//清空
void Clear(PQueue qu)
{
qu->front = qu->rear = 0;
}
//销毁
void Destroy(PQueue qu)
{
free(qu->base);
qu->base = NULL;//避免二次销毁
qu->front = qu->rear;
}
5.循环队列源文件与函数实现
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include "Queue.h"
//增删改查
//初始化
void Init_queue(struct Queue * qu)
{
//assert
qu->base = (ELEM_TYPE *)malloc(MAX_SIZE * sizeof(ELEM_TYPE));
assert(qu->base != NULL);
qu->front = qu->rear = 0;
}
//入队
bool Push(PQueue qu, ELEM_TYPE val)
{
//assert
if(IsFull(qu))
{
return false;
}
qu->base[qu->rear] = val;
//qu->rear++;//error
qu->rear = (qu->rear+1)%MAX_SIZE;
return true;
}
//出队(既要返回出队的值,还有返回操作是否成功,所以还需要一个输出参数)
bool Pop(PQueue qu, ELEM_TYPE *rtval)
{
//assert qu rtval
if(IsEmpty(qu))
{
return false;
}
*rtval = qu->base[qu->front];
//qu->front++;//error
qu->front = (qu->front+1)%MAX_SIZE;
return true;
}
//获取队头值
bool Top(PQueue qu, ELEM_TYPE *rtval)
{
//assert qu rtval
if(IsEmpty(qu))
{
return false;
}
*rtval = qu->base[qu->front];
return true;
}
//判空
bool IsEmpty(PQueue qu)
{
return qu->front == qu->rear;
}
//判满 如果队尾向后再走一步,遇到了队头,此时则认为队列已经满了
bool IsFull(PQueue qu)
{
//assert
return (qu->rear+1)%MAX_SIZE == qu->front;
}
//查找
int Search(PQueue qu, ELEM_TYPE val)
{
//assert
//实际上是一个数组,所以将其遍历一般,即可
for(int i=qu->front; i!=qu->rear; i=(i+1)%MAX_SIZE)
{
if(val == qu->base[i])
{
return i;
}
}
return -1;
}
//获取队列有效长度
int Get_length(PQueue qu)
{
return (qu->rear - qu->front + MAX_SIZE) % MAX_SIZE;
}
//清空
void Clear(PQueue qu)
{
qu->front = qu->rear = 0;
}
//销毁
void Destroy(PQueue qu)
{
free(qu->base);
qu->base = NULL;//避免二次销毁
qu->front = qu->rear;
}
//打印
void Show(PQueue qu)
{
//assert
//实际上是一个数组,所以将其遍历一般,即可
for(int i=qu->front; i!=qu->rear; i=(i+1)%MAX_SIZE)
{
printf("%d ", qu->base[i]);
}
printf("\n");
}