文章目录
- 一 顺序队列
- 1 应用场景
- 2 基本概念
- (1)基本介绍
- (2)队列的顺序实现
- (3)队列的入队和出队操作
- (4)使用数组模拟队列
- 3 代码实现
- (1)初始化队列
- (2)判断队列是否已满
- (3)判断队列是否为空
- (4)进队
- (5)出队
- (6)查看队列中元素
- (7)获取队头元素
- 4 完整代码
- 二 环形队列
- 1 基本概念
- 2 环形队列的几种状态
- 3 环形队列的四要素
- 4 代码实现
- (1)初始化队列
- (2)判断队列是否已满
- (3)判断队列是否为空
- (4)进队
- (5)出队
- (6)查看队列中的元素
- (7)求队列中元素个数
- (8)获得头元素
- 5 完整代码
一 顺序队列
1 应用场景
银行排队、餐厅网上叫号系统使用的都是队列这种数据结构。
2 基本概念
(1)基本介绍
- 队列是一个有序列表,可以用数组或是链表来实现,只允许在一端进行插入,在另一端删除。
- 遵循先入先出的原则。即:先存入队列的数据,要先取出。后存入的要后取出
- 队列示意图如下:
(2)队列的顺序实现
使用数组模拟队列示意图:
(3)队列的入队和出队操作
队列本身是有序列表, 其中 maxSize 是该队列的最大容量。例如当队列最大长度MaxSize=5时:
约定rear总是指向队尾元素,front指向当前队中队头元素的前一位置 。
元素进队,rear增1,元素出队,front增1。
当rear=MaxSize-1时不能再进队。
(4)使用数组模拟队列
存入队列时称为”enQueue”,enQueue 的处理需要有两个步骤:
- 将尾指针往后移:rear+1 , 当front == rear 【空】
- 若尾指针 rear 小于队列的最大下标 maxSize-1,则将数据存入 rear所指的数组元素中,否则无法存入数据。 rear == maxSize - 1[队列满]
3 代码实现
(1)初始化队列
// 执行队列的初始化操作
public ArrayQuene(int maxSize) {
this.maxSize = maxSize;
rear = -1; // 队尾元素
front = -1; // 队头元素
arr = new int[maxSize];
}
(2)判断队列是否已满
// 判断队列是否已满
public boolean isFull(){
return rear == maxSize - 1;
}
(3)判断队列是否为空
// 判断队列是否为空
public boolean isEmpty(){
return rear == front;
}
(4)进队
// 将数据添加到队列
public void enQuene(int data){
// 判断队列是否已满
if(!isFull()){
arr[++rear] = data;
System.out.println("进队成功!");
} else {
System.out.println("队列已满,不能添加数据!");
}
}
(5)出队
// 获取队列元素,出队
public void deQuene(){
if(!isEmpty()){
System.out.println(arr[++front]);
System.out.println("元素:" + arr[front] + "出队成功!");
}else{
System.out.println("队列为空,出队失败!");
}
}
(6)查看队列中元素
// 输出队列中的所有值
public void showQuene(){
if(!isEmpty()){
for(int i = 0; i < arr.length;i++){
if(i > front && i <= rear){
System.out.print(arr[i] + " ");
}
}
}else{
System.out.println("队列为空!");
}
}
(7)获取队头元素
// 获取队头元素
public void getHead(){
if(!isEmpty()){
System.out.println("队头元素为:" + arr[front + 1 ] );
}else{
System.out.println("队列为空,无队头元素!");
}
}
4 完整代码
public static void main(String[] args) {
ArrayQuene arrayQuene = new ArrayQuene(5);
String key = null;
Scanner sc = new Scanner(System.in);
boolean index = true;
while(index){
System.out.println("============================");
System.out.println("show:显示队列");
System.out.println("en :进队");
System.out.println("de :出队");
System.out.println("head:显示队头");
System.out.println("exit:退出程序");
System.out.println("============================");
key = sc.nextLine();
switch (key){
case "show":
arrayQuene.showQuene();
break;
case "en":
System.out.println("请输入进队元素");
int data = sc.nextInt();
arrayQuene.enQuene(data);
break;
case "de":
arrayQuene.deQuene();
break;
case "head":
arrayQuene.getHead();
break;
case "exit":
sc.close();
index = false;
break;
default:
break;
}
}
System.out.println("程序已退出!");
}
public static class ArrayQuene{
private int maxSize;
private int front;
private int rear;
private int[] arr; // 存储数据的值
// 执行队列的初始化操作
public ArrayQuene(int maxSize) {
this.maxSize = maxSize;
rear = -1; // 队尾元素
front = -1; // 队头元素
arr = new int[maxSize];
}
// 判断队列是否已满
public boolean isFull(){
return rear == maxSize - 1;
}
// 判断队列是否为空
public boolean isEmpty(){
return rear == front;
}
// 将数据添加到队列
public void enQuene(int data){
// 判断队列是否已满
if(!isFull()){
arr[++rear] = data;
System.out.println("进队成功!");
} else {
System.out.println("队列已满,不能添加数据!");
}
}
// 获取队列元素,出队
public void deQuene(){
if(!isEmpty()){
System.out.println(arr[++front]);
System.out.println("元素:" + arr[front] + "出队成功!");
}else{
System.out.println("队列为空,出队失败!");
}
}
// 输出队列中的所有值
public void showQuene(){
if(!isEmpty()){
for (int i : arr) {
System.out.println(i + " ");
}
}else{
System.out.println("队列为空!");
}
}
// 获取队头元素
public void getHead(){
if(!isEmpty()){
System.out.println("队头元素为:" + arr[front + 1 ] );
}else{
System.out.println("队列为空,无队头元素!");
}
}
}
二 环形队列
1 基本概念
使用顺序队列有一个问题:当队满之后,经过几次出队操作后,队列中虽然存在空位置,但是不能执行进队操作,具体情形如下图:
这是因为采用rear==MaxSize-1作为队满条件的缺陷。当队满条件为真时,队中可能还有若干空位置。
这种溢出并不是真正的溢出,称为假溢出。
解决方案就是把数组的前端和后端连接起来,形成一个环形的顺序表,即把存储队列元素的表从逻辑上看成一个环,称为环形队列或循环队列。
实际上内存地址一定是连续的,不可能是环形的,这里是通过逻辑方式实现环形队列,也就是将rear++和front++改为:
- rear=(rear+1)%MaxSize
- front=(front+1)%MaxSize
2 环形队列的几种状态
现在约定rear=front为队空,以下两种情况都满足该条件:
并约定(rear+1)%MaxSize=front为队满条件
当进队一个元素就到达队头时,就认为队满了。这样做会少放一个元素,牺牲一个元素没关系的
3 环形队列的四要素
队空条件:front = rear
队满条件:(rear+1)%MaxSize = front
进队e操作:rear=(rear+1)%MaxSize; 将e放在rear处
出队操作:front=(front+1)%MaxSize; 取出front处元素e;
在环形队列中,实现队列的基本运算算法与非环形队列类似,只是改为上述4要素即可。
4 代码实现
(1)初始化队列
// 初始化环形队列
public CircleArray(int maxSize){
this.maxSize = maxSize;
// 定义front执行环形队列的第一个元素,而非顺序队列中执行队头的前一个元素,rear同理
front = 0;
rear = 0;
arr = new int[maxSize];
}
(2)判断队列是否已满
// 判断是否满
public boolean isFull(){
return (rear + 1 ) % maxSize == front;
}
(3)判断队列是否为空
// 判断是否为空
public boolean isEmpty(){
return rear == front;
}
(4)进队
// 进队
public void enQuene(int data){
if(!isFull()){
// 注意与顺序队列的区别
arr[rear++] = data;
rear = rear % maxSize;
System.out.println("元素:" + data + "进队成功!");
}else {
System.out.println("队列已满,进队失败!");
}
}
(5)出队
// 出队
public void deQuene(){
if(!isEmpty()){
System.out.println("元素:" + arr[front++] + "出队成功!");
front = front % maxSize;
}else{
System.out.println("队列为空,出队失败!");
}
}
(6)查看队列中的元素
// 输出队列中的元素
public void showQuene(){
if(!isEmpty()){
// front从零开始
for(int i = front; i < front + size(); i++) {
// 当i大于rear时,说明队列已满且经过出队操作
// 针对这种情况,输出rear后所有元素
if ( (i >= front && i < rear) || (i > rear) ) {
System.out.print(arr[i % maxSize] + " ");
}
}
}else{
System.out.println("队列为空!");
}
}
(7)求队列中元素个数
// 求出数据中的元素个数,从1开始
public int size(){
return (rear + maxSize - front) % maxSize;
}
(8)获得头元素
// 获取头元素
public void getHead(){
System.out.println("队头元素为:" + arr[front % maxSize]);
}
5 完整代码
package com.hzy.datastructs;
import java.util.Scanner;
public class CircleArrayDemo {
public static void main(String[] args) {
CircleArray circleArray = new CircleArray(5);
String key = null;
Scanner sc = new Scanner(System.in);
boolean index = true;
while(index){
System.out.println("============================");
System.out.println("show:显示队列");
System.out.println("en :进队");
System.out.println("de :出队");
System.out.println("head:显示队头");
System.out.println("exit:退出程序");
System.out.println("============================");
key = sc.nextLine();
switch (key){
case "show":
circleArray.showQuene();
break;
case "en":
System.out.println("请输入进队元素");
int data = sc.nextInt();
circleArray.enQuene(data);
break;
case "de":
circleArray.deQuene();
break;
case "head":
circleArray.getHead();
break;
case "size":
circleArray.size();
break;
case "exit":
sc.close();
index = false;
break;
default:
break;
}
}
System.out.println("程序已退出!");
}
public static class CircleArray{
private int[] arr;
private int front;
private int rear;
private int maxSize;
// 初始化环形队列
public CircleArray(int maxSize){
this.maxSize = maxSize;
// 定义front执行环形队列的第一个元素,而非顺序队列中执行队头的前一个元素,rear同理
front = 0;
rear = 0;
arr = new int[maxSize];
}
// 判断是否满
public boolean isFull(){
return (rear + 1 ) % maxSize == front;
}
// 判断是否为空
public boolean isEmpty(){
return rear == front;
}
// 进队
public void enQuene(int data){
if(!isFull()){
// 注意与顺序队列的区别
arr[rear++] = data;
rear = rear % maxSize;
System.out.println("元素:" + data + "进队成功!");
}else {
System.out.println("队列已满,进队失败!");
}
}
// 出队
public void deQuene(){
if(!isEmpty()){
System.out.println("元素:" + arr[front++] + "出队成功!");
front = front % maxSize;
}else{
System.out.println("队列为空,出队失败!");
}
}
// 输出队列中的元素
public void showQuene(){
if(!isEmpty()){
// front从零开始
for(int i = front; i < front + size(); i++) {
// 当i大于rear时,说明队列已满且经过出队操作
// 针对这种情况,输出rear后所有元素
if ( (i >= front && i < rear) || (i > rear) ) {
System.out.print(arr[i % maxSize] + " ");
}
}
}else{
System.out.println("队列为空!");
}
}
// 求出数据中的元素个数,从1开始
public int size(){
return (rear + maxSize - front) % maxSize;
}
// 获取头元素
public void getHead(){
System.out.println("队头元素为:" + arr[front % maxSize]);
}
}
}