队列学习笔记

队列

概念

队列是一个有序列表,可以用数组或者链表实现,且遵循先进先出的原则。

图示如下:

数组模拟队列思路

1)队列本身是有序列表,若使用数组的结构来存储队列中的数据,则应声明以下变量:

private int maxSize;//表示数组的最大容量
private int front;//队列头
private int rear;//队列尾
private int[] arr;//该数组用于存放数据,模拟队列

2)用代码实现以下功能:

  • addQueue

    将尾指针往后移:rear+1(当front==rear时队列为空)

    若尾指针rear小于队列最大下标maxSize-1,则将数据存入rear所指的数组元素中(rear==maxSize-1时队列满,无法存入元素)

    //添加数据到队列
    	public void addQueue(int n) {
    		//首先判断队列是否满
    		if(isFull()) {
    			System.out.println("队列满,不能添加数据");
    			return;
    		}
    		rear++;
    		arr[rear] = n;
    	}
    
  • getQueue

    //取出队列数据(出队列)
    	public int getQueue() {
    		//首先判断队列是否空
    		if(isEmpty())
    			//通过抛出异常来处理
    			throw new RuntimeException("队列为空,无法取出数据");
    		front++;
    		return arr[front];
    	}
    
  • showQueue

    //显示队列所有数据
    	public void showQueue() {
    		//首先判断队列是否空
    		if(isEmpty()) {
    			System.out.println("队列空");
    			return;
    		}
    		for (int i = 0; i < arr.length; i++) {
    			System.out.printf("arr[%d]=%d\n",i,arr[i]);
    		}
    	}
    
  • showHead

    //显示队列头数据
    	public int showHead() {
    		if(isEmpty())
    			throw new RuntimeException("队列空");
    		return arr[front + 1];
    	}
    

完整代码如下

package com.queue;

import java.util.Scanner;

public class ArrayQueueDemo {

	public static void main(String[] args) {
		//测试
		//创建一个队列
		ArrayQueue queue = new ArrayQueue(3);
		Scanner sc = new Scanner(System.in);
		char key;//接收用户输入
		boolean loop = true;
		
		//菜单
		while(loop) {
			System.out.println("s(show): 显示队列");
			System.out.println("e(exit): 退出程序");
			System.out.println("a(add): 添加数据到队列");
			System.out.println("g(get): 从队列中取出数据");
			System.out.println("h(head): 查看队列头的数据");
			
			key = sc.next().charAt(0);
			switch(key) {
			case 's':
				queue.showQueue();
				break;
			case 'a':
				System.out.println("请输入一个数:");
				int value = sc.nextInt();
				queue.addQueue(value);
				break;
			case 'g'://取出数据
				try {
					int res = queue.getQueue();
					System.out.printf("取出的数据为:%d\n",res);
				} catch (Exception e) {
					System.out.println(e.getMessage());
				}
				break;
			case 'h'://查看队列头数据
				try {
					int res = queue.showHead();
					System.out.printf("队列头的数据为:%d\n",res);
				} catch (Exception e) {
					System.out.println(e.getMessage());
				}
				break;
			case 'e'://退出程序
				sc.close();
				loop = false;
				break;
			default:
				break;
			}
		}
		System.out.println("程序退出");
	}

}

//使用数组模拟队列,编写一个ArrayQueue类
class ArrayQueue {
	private int maxSize;//表示数组的最大容量
	private int front;//队列头
	private int rear;//队列尾
	private int[] arr;//该数组用于存放数据,模拟队列
	
	//创建队列的构造器
	public ArrayQueue(int arrMaxSize) {
		maxSize = arrMaxSize;
		front = -1; //指向队列头的前一个位置
		rear = -1;//指向队列尾部数据(即队列最后一个数据)
		arr = new int[maxSize];
	}
	
	//判断队列是否满
	public boolean isFull() {
		return rear == maxSize - 1;
	}
	
	//判断 队列是否为空
	public boolean isEmpty() {
		return rear == front;
	}
	
	//添加数据到队列
	public void addQueue(int n) {
		//首先判断队列是否满
		if(isFull()) {
			System.out.println("队列满,不能添加数据");
			return;
		}
		rear++;
		arr[rear] = n;
	}
	
	//取出队列数据(出队列)
	public int getQueue() {
		//首先判断队列是否空
		if(isEmpty())
			//通过抛出异常来处理
			throw new RuntimeException("队列为空,无法取出数据");
		front++;
		return arr[front];
	}
	
	//显示队列所有数据
	public void showQueue() {
		//首先判断队列是否空
		if(isEmpty()) {
			System.out.println("队列空");
			return;
		}
		for (int i = 0; i < arr.length; i++) {
			System.out.printf("arr[%d]=%d\n",i,arr[i]);
		}
	}
	
	//显示队列头数据
	public int showHead() {
		if(isEmpty())
			throw new RuntimeException("队列空");
		return arr[front + 1];
	}
}

已知问题

代码中创建的数组只能使用一次,无法达到复用效果。

运行时先添加了10,20,30三个数据进入队列,然后依次用getQueue()方法取出,再添加数据时会显示“队列满,不能添加数据”。

解决方法

环形队列

  • 将数组看作环形(通过取模实现)

  • 更改rear和front两个变量的含义,便于算法实现

    /*
    * 令front指向队列的第一个元素,也就是说arr[front]就是队列的第一个元素。
    *  front初始值为0.
    */
    private int front;//队列头
    /*
    * 令rear指向队列的最后一个元素的后一个位置。
    *  rear初始值为0.
    */
    private int rear;//队列尾
    
  • 分析可知:

    • 队列满的条件是(rear + 1) % maxSize = front

    • 队列空的条件是front == rear

    • 队列中有效数据个数为(rear + maxSize - front) % maxSize

      //统计队列有效数据个数
      public int size() {
      	return (rear + maxSize - front) % maxSize;
      }
      
  • 注意:取数据时,考虑到front需要后移,需要使用以下几个步骤

    • 将front所指的值赋给一个临时变量

    • 将front后移

    • 将临时变量的值返回

      int value = arr[front];
      front = (front + 1) % maxSize;
      return value;
      

具体代码实现与普通队列稍有差异,如下:

package com.queue;

import java.util.Scanner;

public class CirlceArrayQueue {

	public static void main(String[] args) {
		//测试
		// 创建一个队列
		CirlceArray queue = new CirlceArray(4);//最大有效数据为3
		Scanner sc = new Scanner(System.in);
		char key;// 接收用户输入
		boolean loop = true;

		// 菜单
		while (loop) {
			System.out.println("s(show): 显示队列");
			System.out.println("e(exit): 退出程序");
			System.out.println("a(add): 添加数据到队列");
			System.out.println("g(get): 从队列中取出数据");
			System.out.println("h(head): 查看队列头的数据");

			key = sc.next().charAt(0);
			switch (key) {
			case 's':
				queue.showQueue();
				break;
			case 'a':
				System.out.println("请输入一个数:");
				int value = sc.nextInt();
				queue.addQueue(value);
				break;
			case 'g':// 取出数据
				try {
					int res = queue.getQueue();
					System.out.printf("取出的数据为:%d\n", res);
				} catch (Exception e) {
					System.out.println(e.getMessage());
				}
				break;
			case 'h':// 查看队列头数据
				try {
					int res = queue.showHead();
					System.out.printf("队列头的数据为:%d\n", res);
				} catch (Exception e) {
					System.out.println(e.getMessage());
				}
				break;
			case 'e':// 退出程序
				sc.close();
				loop = false;
				break;
			default:
				break;
			}
		}
		System.out.println("程序退出");
	}

}

class CirlceArray {
	private int maxSize;//表示数组的最大容量
	/*
	 * 令front指向队列的第一个元素,也就是说arr[front]就是队列的第一个元素。
	 *  front初始值为0.
	 */
	private int front;//队列头
	/*
	 * 令rear指向队列的最后一个元素的后一个位置。
	 *  rear初始值为0.
	 */
	private int rear;//队列尾
	private int[] arr;//该数组用于存放数据,模拟队列
	
	public CirlceArray(int arrMaxSize) {
		maxSize = arrMaxSize;
		arr = new int[maxSize];
	}
	
	//判断队列是否满
		public boolean isFull() {
			return (rear + 1) % maxSize == front;
		}
		
		//判断 队列是否为空
		public boolean isEmpty() {
			return rear == front;
		}
		
		//添加数据到队列
		public void addQueue(int n) {
			//首先判断队列是否满
			if(isFull()) {
				System.out.println("队列满,不能添加数据");
				return;
			}
			//直接将n加入队列
			arr[rear] = n;
			//将rear后移,必须考虑取模
			rear = (rear + 1) % maxSize;
		}
		
		//取出队列第一个数据(出队列)
		public int getQueue() {
			//首先判断队列是否空
			if(isEmpty())
				//通过抛出异常来处理
				throw new RuntimeException("队列为空,无法取出数据");
			//考虑到front是直接指向队列的第一个元素,需要用以下步骤取出数据
			//1.将front所指的值赋给一个临时变量
			//2.将front后移
			//3.将临时变量的值返回
			int value = arr[front];
			front = (front + 1) % maxSize;
			return value;
		}
		
		//统计队列有效数据个数
		public int size() {
			return (rear + maxSize - front) % maxSize;
		}
		//显示队列所有数据
		public void showQueue() {
			//首先判断队列是否空
			if(isEmpty()) {
				System.out.println("队列空");
				return;
			}
			for (int i = front; i < front + size(); i++) {
				System.out.printf("arr[%d]=%d\n",i % maxSize,arr[i % maxSize]);
			}
		}
		
		//显示队列头数据
		public int showHead() {
			if(isEmpty())
				throw new RuntimeException("队列空");
			return arr[front];
		}
}

posted @ 2020-09-09 21:34  老陈歌歌  阅读(210)  评论(0)    收藏  举报