• 第二章 队列


    1 基本概念

    队列是一种先进先出(First in First Out)的线性表,简称FIFO。队列中,只允许在一端进行插入操作,而在另一端进行删除操作。允许插入的一端称为队尾,允许删除的一端称为队头。

    如下图所示,假设队列q=(a1,a2,…,an),那么a1就是队头元素,而an是队尾元素。这样删除元素时,总是从a1开始,而插入时,总是在队列尾部插入。

    1507784-20190716140330407-1176335956

    2 数组模拟简单队列

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

    image-20200824233209504

    上图中,变量front与rear的初始值均为0,这表明front与rear相等时,队列为空。当rear==MaxSize成立时,队列为满。需要注意的是,front指向队列头部元素位置,而rear指向队列尾部后一个元素的位置。

    简单队列的java实现

    package com.victor.queue;
    import java.util.Scanner;
    
    //数组模拟简单队列
    public class ArrayQueueDemo {
    
    	public static void main(String[] args) {
    		ArrayQueue aq = new ArrayQueue(3);
    		char key = ' '; //接收用户输入
    		Scanner scanner = new Scanner(System.in);
    		boolean loop = true;
    		//输出一个菜单栏
    		while(loop){
    			System.out.println("s(show): 打印队列");
    			System.out.println("a(add): 入队列");
    			System.out.println("g(get): 出队列");
    			System.out.println("h(head): 打印队头");
    			System.out.println("e(exit): 退出程序");
    			key = scanner.next().charAt(0);
    			switch (key) {
    			case 's':
    				aq.showQueue();
    				break;
    			case 'a': //入队列
    				try {
    					System.out.println("请输入一个整数");
    					int value = scanner.nextInt();
    					aq.addQueue(value);  //最好判断一下value是不是整数
    				} catch (Exception e) {
    					// TODO: handle exception
    					System.out.println(e.getMessage());
    				}
    				break;
    			case 'g': //出队列
    				try {
    					int res = aq.getQueue();
    					System.out.printf("出队列的整数为%d
    ", res);
    				} catch (Exception e) {
    					// TODO: handle exception
    					System.out.println(e.getMessage()); //输出getQueue()方法中定义好的异常信息
    				}
    				break;
    			case 'h': //打印队头
    				try {
    					int res = aq.headQueue();
    					System.out.printf("队列头的整数为%d
    ", res);
    				} catch (Exception e) {
    					// TODO: handle exception
    					System.out.println(e.getMessage());
    				}
    				break;
    			case 'e': //退出
    				scanner.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;
    		arr = new int[maxSize];
    		front = 0;  //队列头部
    		rear = 0;  //队列尾部,指向队列尾部后一个元素的位置,front=rear时队列为空
    	}
    	
    	//判断队列是否为满队列
    	public boolean isFull(){
    		return rear == maxSize;
    	}
    	
    	//判断队列是否为空队列
    	public boolean isEmpty(){
    		return rear == front;
    	}
    	
    	//入队列
    	public void addQueue(int n){
    		if(isFull()){
    			throw new RuntimeException("队列满,不能添加数据了");
    		}
    		arr[rear] = n;
    		rear++; //rear后移
    	}
    	
    	//出队列
    	public int getQueue(){
    		if(isEmpty()){
    			//抛出异常
    			throw new RuntimeException("队列空,不能出队列了");
    		}
    		int value = arr[front];
    		front++; //front后移
    		return value;
    
    	}
    	
    	//打印队列
    	public void showQueue(){
    		if(isEmpty()){
    			System.out.println("队列空");
    			return;
    		}
    		// 打印队列中的元素,不是打印数组中的所有元素
    		for (int i = front; i < rear; i++){
    			System.out.printf("arr[%d]=%d
    ",i, arr[i]); //格式化输出
    		}
    	}
    		
    	
    	//打印队头,不是出队列
    	public int headQueue(){
    		if (isEmpty()){
    			//抛出异常
    			throw new RuntimeException("队列空,不能打印头");
    		}
    		return arr[front];  
    	}
    }
    

    3 数组模拟循环队列

     如下图a所示,当front指向下标为2的数组位置,rear指向下标为4的数组位置时,若向队列尾部再添加一个元素(a_{5}),则rear指针会越界,如下图b所示。但此时队列前部仍然有空间可以存储,所以简单队列的弊端就是不能充分利用数组空间。

    image-20200825173213969

    图a

    image-20200825172934389

    图b

    为了解决数组空间不够用的办法就是后面满了,就再从头开始,也就是头尾相接的循环。我们把队列的这种头尾相接的顺序存储结构称为循环队列。比如上图b中,我们把rear置为0即可解决问题。
     我们只需对前面的数组模拟简单队列进行优化,即可充分利用数组空间。 因此将数组看做是一个环形的,通过对front和rear指针取模实现指针循环。以下图为例进行说明:

    image-20200824233209504

    变化如下:

    • front变量:指向队列的第一个元素,初始值为0。移动情况为front = (front + 1)% maxSize

    • rear变量:指向队列的最后一个元素的后一个位置,因为希望空出一个空间做为约定,rear的初始值为0。移动情况为rear = (rear + 1)% maxSize

    • 当队列满时,条件是(rear +1)% maxSize == front成立。因为空出了一个位置,故需要rear+1;

    • 当队列为空时,rear == front成立;

    • 队列中有效数据的个数:这里有两种情况,分别讨论:

    (1)当front < rear 时,如下图,队列中的有效数据个数 = rear - front,这里rear-front = 2。

    image-20200825173213969

    (2)当front > rear 时,如下图,队列中的有效数据个数=front右边的数据个数+rear左边的数据个数,又有:

    • front右边的数据个数 = maxSize - front,这里maxSize - front = 3,maxSzie为数组的最大容量;
    • rear左边的数据个数 = rear - 0 = rear,这里为0;

    所以队列中的有效数据个数 = rear - front + maxSize

    image-20200825204541913

    综合两种情况可得:

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

    循环队列的java实现

    package com.victor.queue;
    import java.util.Scanner;
    
    //数组模拟循环队列
    public class CircleArrayQueueDemo {
    
    	public static void main(String[] args) {
    		CircleQueue cq = new CircleQueue(4); //有效数据3个
    		char key = ' '; //接收用户输入
    		Scanner scanner = new Scanner(System.in);
    		boolean loop = true;
    		//输出一个菜单栏
    		while(loop){
    			System.out.println("s(show): 打印队列");
    			System.out.println("a(add): 入队列");
    			System.out.println("g(get): 出队列");
    			System.out.println("h(head): 打印队头");
    			System.out.println("e(exit): 退出程序");
    			key = scanner.next().charAt(0);
    			switch (key) {
    			case 's':
    				cq.showQueue();
    				break;
    			case 'a': //入队列
    				try {
    					System.out.println("请输入一个整数");
    					int value = scanner.nextInt();
    					cq.addQueue(value);  //最好判断一下value是不是整数
    				} catch (Exception e) {
    					// TODO: handle exception
    					System.out.println(e.getMessage());
    				}
    				break;
    			case 'g': //出队列
    				try {
    					int res = cq.getQueue();
    					System.out.printf("出队列的整数为%d
    ", res);
    				} catch (Exception e) {
    					// TODO: handle exception
    					System.out.println(e.getMessage()); //输出getQueue()方法中定义好的异常信息
    				}
    				break;
    			case 'h': //打印队头
    				try {
    					int res = cq.headQueue();
    					System.out.printf("队列头的整数为%d
    ", res);
    				} catch (Exception e) {
    					// TODO: handle exception
    					System.out.println(e.getMessage());
    				}
    				break;
    			case 'e': //退出
    				scanner.close();
    				loop = false;
    				break;
    			default:
    				break;
    			}
    		}
    		System.out.println("程序退出");
    	}
    }
    
    
     //使用数组模拟循环队列,CircleQueue类
    class CircleQueue{
    	private int maxSize; //数组的最大容量
    	private int front;   //队列头
    	private int rear;   //队列尾,指向队列尾部后一个元素的位置
    	private int[] arr;  //该数组用于存放数据,模拟队列
    	
    	//队列的构造方法
    	public CircleQueue(int arrMaxSize){
    		maxSize = arrMaxSize;
    		arr = new int[maxSize];
    		front = 0;  //队列头部
    		rear = 0;  //队列尾部,指向队列尾部后一个元素的位置,front=rear时队列为空
    	}
    	
    	//判断队列是否为满队列,因为预留了一个空间,所以要rear+1
    	public boolean isFull(){
    		return front == (rear + 1) % maxSize;
    	}
    	
    	//判断队列是否为空队列,跟简单队列的条件一样
    	public boolean isEmpty(){
    		return rear == front;
    	}
    	
    	//入队列
    	public void addQueue(int n){
    		if(isFull()){
    			throw new RuntimeException("队列满,不能添加数据了");
    		}
    		arr[rear] = n;
    		rear = (rear + 1) % maxSize; //rear要循环
    	}
    	
    	//出队列
    	public int getQueue(){
    		if(isEmpty()){
    			//抛出异常
    			throw new RuntimeException("队列空,不能出队列了");
    		}
    		int value = arr[front];
    		front = (front + 1) % maxSize; //front要循环
    		return value;
    
    	}
    	
    	//打印队列
    	public void showQueue(){
    		if(isEmpty()){
    			System.out.println("队列空");
    			return;
    		}
    		// 打印队列中的有效元素,注意这里i的边界条件,size()方法见下面
    		for (int i = front; i < front + size(); i++){
    			int index = i % maxSize; //index为循环后的下标
    			System.out.printf("arr[%d]=%d
    ",index, arr[index]); //格式化输出
    		}
    	}
    		
    	
    	//打印队头,不是出队列
    	public int headQueue(){
    		if (isEmpty()){
    			//抛出异常
    			throw new RuntimeException("队列空,不能打印头");
    		}
    		return arr[front];  
    	}
    	
    	//求出当前队列的有效数据个数
    	public int size(){
    		return (rear - front + maxSize) % maxSize;
    	}
    }
    

    reference

    韩顺平数据结构

    大话数据结构

  • 相关阅读:
    python库--pandas--文本文件读取
    python库--flashtext--大规模数据清洗利器
    PyCharm--帮助文档
    Git--命令
    symfony doctrine generate entity repository
    [转]MySQL性能优化的最佳20+条经验
    svn使用
    一致性hash
    JavaScript学习笔记 1
    curl发出请求
  • 原文地址:https://www.cnblogs.com/victorxiao/p/13562414.html
Copyright © 2020-2023  润新知