数据结构(Java实现)-详谈队列

 

1、队列介绍

2、队列的顺序存储(数组实现)

2.1队列的相关概念

2.2队列的操作

2.3代码实现

3、队列的链式存储(链表实现)

3.1链式队列的入队

3.2链式队列的出队

3.3代码实现:

4、循环队列

4.1顺序队列假溢出问题引出循环队列:

4.2何谓循环队列?

4.3循环队列的实现过程


1、队列介绍

  • 队列是一个有序列表,可以用数组(顺序存储)或是链表(链式存储)来实现
  • 遵循先入先出(从队尾进,队首出)的原则。即:先存入队列的数据,要先取出,后存入的要后取出。

2、队列的顺序存储(数组实现)

队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如下图, 其中 maxSize 是该队列的最大容量。 因为队列的输出、输入是分别从前后端来处理,因此需要两个变量 front及 rear分别记录队列前后端的下标,front 会随着数据输出而改变,而 rear则是随着数据输入而改变。

使用数组模拟队列示意图:

2.1队列的相关概念

  1. 队头与队尾:允许元素插入的一端称为队尾,允许元素删除的一端称为队头。
  2. 入队:队列的插入操作
  3. 出队:队列的删除操作

下面用两张图来理解入队和出队:

例如我们有一个存储整型元素的队列,我们依次入队:{1,2,3}:

添加元素时,元素只能从队尾一端进入队列,也即是2只能跟在1后面,3只能跟在2后面。

如果要出队:元素只能从队首出队列,出队列的顺序为:1、2、3,与入队时的顺序一致,这就是所谓的“先进先出”。

2.2队列的操作

队列通常提供的操作:

  1. 入队
  2. 出队
  3. 输出所有队列元素
  4. 获取队首元素
  5. 判断队满或队空

2.3代码实现

package com.zhukun.ArrayListQueue;

import java.util.Scanner;

//使用数组模拟队列-编写一个ArrayQueue类
class ArrayQueue{
	private int maxSize;//表示数组的最大容量
	private int front;//队列头
	private int rear;//队列尾
	private int[] arr;//该数据用于存放数据,模拟队列
	private int length=0;//队列中实际元素的数量
	//创建队列构造器
	public ArrayQueue(int arrMaxSize)
	{
		this.maxSize = arrMaxSize;
		this.arr = new int[maxSize];
		this.front = -1;//指向队列头部,分析出front是指向队列头的前一个位置
		this.rear = -1;//指向队列的尾部,指向队列尾的数据(即就是队列最后一个数据)
	}
	//判断队列是否满
	public boolean isFull()
	{
		return rear == maxSize-1;//如果成立则队满,即rear指向容量满足下最后一个数据
	}
	//判断是否为空
	public boolean isEmpty()
	{
		return rear == front;//如果成立则对空,即rear指向队首front说明队列为空
	}
	//入队
	public void addQueue(int num) {
		//先判断队列是否为满
		if(isFull()) {
			System.out.println("队列已满,不能加入数据");
			return;
		}
		rear++;//rear后移
		arr[rear] = num;
	}
	//出队
	public int getQueue() {
		//判断队列是否为空
		if(isEmpty()) {
			//抛出异常
			throw new RuntimeException("队列为空,不能取数据");
		}
		front++;//front 后移	
		return arr[front];
	}
	//显示队列的所有元素
	public void showQueue() {
		//遍历
		if(isEmpty()) {
			System.out.println("队列空的,没有数据~");
			return;
		}
		for(int i=front+1;i<rear+1;i++)
		{
			System.out.printf("arr[%d]=%d\n",i,arr[i]);
		}
	}
	//显示队列的头数据,注意不是提取数据
	public int headQueue() {
		int num;//记录队首元素
		//判断
		if(isEmpty())
		{
			throw new RuntimeException("队列空的,没有数据");
		}
		num = arr[front+1];
		return num;
	}
	//获取当前队列的元素个数
	public int getLength()
	{
		return rear-front;
		
	}
}
public class ArrayQueueDemo {
	public static void main(String[] args)
	{
		//测试
		//创建一个队列
		ArrayQueue queue = new ArrayQueue(6);
		int n;//接收用户输入
		Scanner scanner = new Scanner(System.in);
		boolean loop = true;
		//输出一个菜单
		while(loop)
		{
			System.out.println("1、输出队列");
			System.out.println("2、退出程序");
			System.out.println("3、入队");
			System.out.println("4、出队");
			System.out.println("5、查看队头数据");
			System.out.println("6、输出队列的元素数量");
			System.out.println("请输入你的选择:");
			n = scanner.nextInt();
			switch(n)
			{
			case 1:
				queue.showQueue();
				break;
			case 2:
				scanner.close();
				loop = false;
				break;
			case 3:
				System.out.print("请输入要入队的数据:");
				int value = scanner.nextInt();
				queue.addQueue(value);
				break;
			case 4:
				try {
					int res = queue.getQueue();
					System.out.println("取出的数据是:"+res);
				}catch(Exception e)
				{
					System.out.println(e.getMessage());
				}
				break;
			case 5:
				try {
					int res = queue.headQueue();
					System.out.println("队列头的数据为:"+res);
				}catch(Exception e)
				{
					System.out.println(e.getMessage());
				}
				break;
			case 6:
				int l = queue.getLength();
				System.out.println("队列中元素个数为:"+l);
				break;
			}
		}
	}

}

3、队列的链式存储(链表实现)

类似于使用链式结构保存线性表,也可以采用链式结构来保存队列的元素,采用链式存储结构的队列也被称为链队列。
对于链队列而言,由于程序需要从rear端添加元素,然后从front端删除元素,因此考虑对链队列增加front、rear两个引用变量,使他们分别指向链队列的头、尾两个节点。如下图所示:

注意:由于链队列采用链式存储结构保存数据元素,该队列允许添加无限多个数据元素,因此链队列不会出现列满的问题。

3.1链式队列的入队

对于链队列而言,插入操作的实现非常简单,只要创建一个新节点,让原rear节点的next指向新节点,在让rear指向新节点即可。如下图所示链队列的插入操作:

3.2链式队列的出队


对于链队列而言,删除操作的实现也是非常的简单,只要将原front节点指向原front节点的next节点,当然不要忘记释放原front节点的引用。如下图所示链队列的移除操纵:

3.3代码实现:

package com.zhukun.Queue;

import java.util.Scanner;

class LinkQueue <T> {
	//创建一个私有的结点类,一个链式结点有数据域和指针域
	private class Node{
		private T data;      //T类型的数据
		private Node next;   //结点类型的指针 指向下一个结点
		//无参构造函数
		public Node(){}
		//有参构造函数
		public Node(T element, Node next){
			this.data = element;
			this.next = next;
		}
	}
	//代表链式队列的大小
	private int size;
	//链式队列的链队首
	private Node front;
	//链式队列的链队尾
	private Node rear;
	
	//初始化链式队列
	//链式队列无参构造函数
	public LinkQueue(){
		size = 0;
		front = null;
		rear = null;
	}
	//链式队列含参构造函数
	public LinkQueue(T element){
		rear = new Node(element, null);
		front = rear;
		size ++;
	}
	//返回链式队列的长度
	public int getLength(){
		return size;
	}
	//判断队列是否为空
	public boolean isEmpty(){
		return size == 0;
	}
	//向rear端队尾插入元素,入队
	public void addQueue(T element){
		if(isEmpty()){
			rear = new Node(element, null);	
			front = rear;
		}else{
			rear.next = new Node(element, null);		
			rear = rear.next;
		}
		size ++;
	}
	//从front端队首移除元素
	public T removeQueue(){
		if(isEmpty()){
			throw new IndexOutOfBoundsException("链式队列为空异常");
		}
		Node oldNode = front;
		front = front.next;
		oldNode.next = null;//释放要移除的结点
		size --;
		return oldNode.data;
	}
	
	//返回链式队列的堆首元素,但不删除
	public T headQueue(){
		return front.data;
	}
	
	//清空链式队列
	public void clear(){
		front = null;
		rear = null;
		size = 0;
	}
	//遍历队列里的全部数据
	public void showQueue() {
		Node p = front;
		while (p!= null)
		{
			System.out.println(p.data);
			p=p.next;
		}
	}
	
}

public class LinkQueueDemo {
	public static void main(String[] args)
	{
		//测试
		//创建一个队列
		LinkQueue<Integer> queue = new LinkQueue<Integer>();
		int n;//接收用户输入
		Scanner scanner = new Scanner(System.in);
		boolean loop = true;
		//输出一个菜单
		while(loop)
		{
			System.out.println("1、输出队列");
			System.out.println("2、退出程序");
			System.out.println("3、入队");
			System.out.println("4、出队");
			System.out.println("5、查看队头数据");
			System.out.println("6、输出队列的元素数量");
			System.out.println("请输入你的选择:");
			n = scanner.nextInt();
			switch(n)
			{
			case 1:
				queue.showQueue();
				break;
			case 2:
				scanner.close();
				queue.clear();
				loop = false;
				break;
			case 3:
				System.out.print("请输入要入队的数据:");
				int value = scanner.nextInt();
				queue.addQueue(value);
				break;
			case 4:
				try {
					int res = queue.removeQueue();
					System.out.println("取出的数据是:"+res);
				}catch(Exception e)
				{
					System.out.println(e.getMessage());
				}
				break;
			case 5:
				try {
					int res = queue.headQueue();
					System.out.println("队列头的数据为:"+res);
				}catch(Exception e)
				{
					System.out.println(e.getMessage());
				}
				break;
			case 6:
				int l = queue.getLength();
				System.out.println("队列中元素个数为:"+l);
				break;
			}
		}
	}
}

4、循环队列

4.1顺序队列假溢出问题引出循环队列:

  1. 初始建立空队列时,令front = rear  = 0
  2. 每当插入新的队列元素时,rear+1
  3. 每当删除队列头元素时,front+1

缺陷:假设当前队列分配的最大空间为6,队列处于(d)状态时不可再继续入队操作,但实际上数组中还有空的位置,这种现象就叫做“假溢出”,解决假溢出的途径--------采用循环队列

4.2何谓循环队列?

首先我们要说明的是循环队列仍然是基于数组实现的。但是为了形象化的说明问题,我们如下图所示:

  1. 图中有两个指针(其实就是两个整数型变量,因为在这里有指示作用,所以这里理解为指针)front、rear,一个指示队头,一个指示队尾。
  2. rear和front互相追赶着,这个追赶过程就是队列添加和删除的过程,如果rear追到front说明队列满了,如果front追到rear说明队列为空。
  3. 令队列空间中的一个单元闲置,使得队列非空时,Q.rear与Q.front之间至少间隔一个空闲单。我们把它掰弯,用的是求余,这样两个值就不会跑出最大范围,并且可以实现弯曲的效果,所以说对于循环队列我们必须给定最大值MAXSIZE。

4.3循环队列的实现过程

我们分析可以发现当front==rear时可能队满也可能队空

此时我们人为浪费一个单元,令队满特征front=(rear+1)%maxSize   ---------空闲单元法

  • 对空条件:front == rear
  • 队满条件:front == (rear+1)% maxsize
  • 队列长度:L=(N+rear-front)% maxsize

代码实现:

package com.zhukun.Queue;

import java.util.Scanner;

//使用数组模拟队列-编写一个ArrayQueue类
class CircularArrayQueue{
	private int maxSize;//表示数组的最大容量
	private int front;//队列头
	private int rear;//队列尾
	private int[] arr;//该数据用于存放数据,模拟队列
	private int length=0;//队列中实际元素的数量
	//创建队列构造器
	public CircularArrayQueue(int arrMaxSize)
	{
		this.maxSize = arrMaxSize;
		this.arr = new int[maxSize];
		this.front = 0;//指向队列头部,分析出front是指向队列头的前一个位置
		this.rear = 0;//指向队列的尾部,指向队列尾的数据(即就是队列最后一个数据)
	}
	//判断队列是否满
	public boolean isFull()
	{
		return (rear+1)%maxSize == front;//如果成立则队满,即rear指向容量满足下最后一个数据
	}
	//判断是否为空
	public boolean isEmpty()
	{
		return rear == front;//如果成立则对空,即rear指向队首front说明队列为空
	}
	//入队
	public void addQueue(int num) {
		//先判断队列是否为满
		if(isFull()) {
			System.out.println("队列已满,不能加入数据");
			return;
		}
		arr[rear] = num;
		//将rear后移,这里要考虑取模
		rear = (rear+1)%maxSize;
	}
	//出队
	public int getQueue() {
		//判断队列是否为空
		if(isEmpty()) {
			//抛出异常
			throw new RuntimeException("队列为空,不能取数据");
		}
		//将front对应的值保留到一个临时变量,将front后移,考虑取模,将临时保存的变量返回
		int value = arr[front];
		front =(front +1)%maxSize;
		return value;
	}
	//显示队列的所有元素
	public void showQueue() {
		//遍历
		if(isEmpty()) {
			System.out.println("队列空的,没有数据~");
			return;
		}
		for(int i=front;i<(rear+maxSize)%maxSize;i++)
		{
			System.out.printf("arr[%d]=%d\n",i,arr[i]);
		}
	}
	//显示队列的头数据,注意不是提取数据
	public int headQueue() {
		int num;//记录队首元素
		//判断
		if(isEmpty())
		{
			throw new RuntimeException("队列空的,没有数据");
		}
		num = arr[front];
		return num;
	}
	//获取当前队列的元素个数
	public int getLength()
	{
		return (rear+maxSize-front)%maxSize;
		
	}
}
public class CircularArrayQueueDemo {
	public static void main(String[] args)
	{
		//测试
		//创建一个队列
		ArrayQueue queue = new ArrayQueue(6);
		int n;//接收用户输入
		Scanner scanner = new Scanner(System.in);
		boolean loop = true;
		//输出一个菜单
		while(loop)
		{
			System.out.println("1、输出队列");
			System.out.println("2、退出程序");
			System.out.println("3、入队");
			System.out.println("4、出队");
			System.out.println("5、查看队头数据");
			System.out.println("6、输出队列的元素数量");
			System.out.println("请输入你的选择:");
			n = scanner.nextInt();
			switch(n)
			{
			case 1:
				queue.showQueue();
				break;
			case 2:
				scanner.close();
				loop = false;
				break;
			case 3:
				System.out.print("请输入要入队的数据:");
				int value = scanner.nextInt();
				queue.addQueue(value);
				break;
			case 4:
				try {
					int res = queue.getQueue();
					System.out.println("取出的数据是:"+res);
				}catch(Exception e)
				{
					System.out.println(e.getMessage());
				}
				break;
			case 5:
				try {
					int res = queue.headQueue();
					System.out.println("队列头的数据为:"+res);
				}catch(Exception e)
				{
					System.out.println(e.getMessage());
				}
				break;
			case 6:
				int l = queue.getLength();
				System.out.println("队列中元素个数为:"+l);
				break;
			}
		}
	}

}

猜你喜欢

转载自blog.csdn.net/weixin_44279178/article/details/107921786