目录
一:什么是队列(Queue)
如同栈一样,队列也是一种线性的逻辑结构。它在表的一端进行插入元素,叫做队尾(tear);在表的一端进行删除元素,叫做队首 (front)。队列就像售票口前的人们站成一队,第一个进入队列的人将最先买到票,最后排队的人最后才能买到票,所以它是一种先进先出的数据结构。
二:假溢出
-
假溢出:队列用的存储区还没有满,但队列却发生了溢出,我们把这种现象称为 “假溢出”。
-
为什么会产生假溢出:
我们使用数组来封装一个队列,进行队首数据移除的时候,队首指针会向后移动,这时数组前面几个位置会空出来。
随着前面空出来位置越来越多,当队尾指针指到最后时候,即便队列还留有空间,但数据还是无法
插入到队列当中,这种现象就成为“假溢出”。
- 如何解决假溢出这种现象?
-
顺序队列:顺序队列是队首数据移除后,把后面位置的数据依次向前移动,补充前面的空位置。这种方法类似我们平常排队买票的方式,前面一个人买完票离开,后面的人跟上去占据前面一个人的位置。显然,采用这种方式,每次队首元素出队列后面的元素的都需要进行向前移动,如果队列元素太多,那么该方法效率将会比较低。
- 循环队列:当队尾指针rear和队首指针front到达存储空间最大值QueueSize时,让队尾指针指向Index=0.虽然在存储上是线形的,但在逻辑上它是一个首尾衔接的环形结构
3. 链表队列:由于链表中存储数据的实际内存并不一定是连续的,所以采用链表队列,不会存在假溢出这种现象。
总结:上诉三种方法顺序队列的时间复杂度:f(n)=O(n) , 循环队列和链表队列时间复杂度:f(n)=O(1)。但是链表在内存上多了指针申请的开销,所以循环队列为更优选择。
三:循环队列的基本操作
- 创建队列 (CreatQueue)
- 入队操作(EnQueue)
- 出队操作(DeQueue)
- 队头元素(GetFrontData)
- 队满判断(IsFull)
- 队空判断(IsEmpty)
- 队的长度(GetLength)
public class MyQueue {
private int MaxValue; //队列最大长度
private int MyArra[];
private int rear; //队尾指针
private int front; //队首指针
private int length; //用length表示队列实际长度
public MyQueue() {
MyArra=new int [10];
front=rear=0;
length=0;
}
public MyQueue(int MaxValue) { //创建队列
this.MaxValue=MaxValue;
MyArra=new int[MaxValue];
front=rear=0;
length=0;
}
public boolean IsFull() { //队满判断
return length>=MaxValue;
}
public boolean IsEmpty() { //队空判断
return length<=0;
}
public void EnQueue(int data) {
if(IsFull()) {
System.out.println("The Queue is full");
return;
}
MyArra[rear]=data; //进队列
rear=(rear+1)%MaxValue; //改变rear
length++; //进队,长度自增
}
public void DeQueue() {
if(IsEmpty()) {
System.out.println("The Queue is empty");
return;
}
MyArra[front]=0; //出队列
front=(front+1)%MaxValue; //改变front
length--; //进队,长度自减少
}
public void show() { //遍历
for(int i=0;i<MyArra.length;i++)
System.out.println(MyArra[i]);
}
public void getLength() { //队的长度
System.out.println("The length of Queue:"+length);
}
public void getFrontData() { //队首元素
System.out.println("frontData is:"+MyArra[front]);
}
}
四:队列的应用问题
- 使用队列实现栈
- 对队列的前k个元素倒序
- 使用队列生成从1到n的二进制数