【数据结构笔记】数组模拟队列 ArrayQueue

队列 Queue


尚硅谷数据结构-学习笔记


-------------队列的基本概念

队列是一个有序列表(Ordered list),可以用数组(Array)和链表(Linked List)来实现

队列遵循先入先出的原则,即:先存入队列的数据,要先取出。后存入的要后取出


【图例1】
【数据结构笔记】数组模拟队列 ArrayQueue_第1张图片
front是队列的头部指针,
rear就是队列的尾部指针
将两个指针的初始值都设为-1
换句话说:
arr[front]指向的是数组第一个数的前一个位置
arr[rear]指向的是数组的最后一个数

当往队列添加数据的时候,尾部指针开始逐1增加,但是头部指针不变;
当从队列中取出数据时,头部指针开始逐1增加,但是尾部指针不变;


-------------数组模拟队列的思路分析

数组的方式模拟队列:

  1. 队列本身是有序列表,若用数组的结构来存储队列的数据
    则队列数组的声明如图例1,其中maxSize就是该队列的最大容量

  2. 因为队列的输出,输入是分别从头部和尾部来处理,因此需要两个变量
    也就是front和rear分别记录队列前后端的下标
    front会随着数据输出而改变,rear随着数据输入而改变

思路分析:

首先是将数据存入队列的“addQueue”, addQueue的处理步骤:

  1. 如果队列完全是空的,那么尾部指针就会指向头部指针,front == rear

  2. 如果队列是满的,即rear == maxSize - 1,那么无法存入数据

  3. 如果队列不是满的,即rear < maxSize - 1
    那么就将数据存入rear所指的数组元素中。


-------------数组模拟队列的代码实现

Example:

import java.util.Scanner;

public class Queue {
	public static void main(String[] args) {
		
		//测试
		
		//创建一个队列
		ArrayQueue queue = new ArrayQueue(3);
		
		char key = ' '; //接受用户输入
		
		Scanner s = new Scanner(System.in);
		boolean loop = true;
		
		while(loop) {
			System.out.println("s(shwo): 显示队列");
			System.out.println("e(exit): 退出程序");
			System.out.println("a(add): 添加数据到队列");
			System.out.println("g(get): 从队列中取出数据");
			System.out.println("h(head): 查看第一个数据");
			
			key = s.next().charAt(0);
			switch(key) {
			
			case 's': 
				queue.showQueue();
				
			case 'e': 
				s.close();
				System.exit(0);
				
			case 'a': 
				System.out.println("请输入一个数字");
				int value = s.nextInt();
				queue.addQueue(value);
				
			case 'g': //取出数据的时候,可能会抛异常,用try catch
				try {
					queue.getQueue();
				}catch(Exception e) {
					System.out.println(e.getMessage());
				}
				
			case 'h': //查看队列第一个数据,可能会抛异常,用try catch
				try {
					queue.headQueue();
				}catch(Exception e) {
					System.out.println(e.getMessage());
				}
			}
		}
	}
}

class ArrayQueue{
	private int maxSize; //表示数组最大容量
	private int front; //队列头
	private int rear; //队列尾
	private int[] arr; //该数据用于存放数据
	
	//constructor
	public ArrayQueue(int arrMaxSize) {
		maxSize = arrMaxSize;
		arr = new int[maxSize];
		front = -1; //指向队列头部前一个位置
		rear = -1; //指向队列尾部(就是队列最后一个数据)
		
		
	}
	
	//判断队列是否满
	public boolean isFull(){
		return rear == maxSize -1;
	}
	
	//判断是否为空
	public boolean isEmpty() {
		return front == rear;
	}
	
	//添加数据到队列
	public void addQueue(int n) {
		
		//首先判断是否满
		if(isFull()) {
			System.out.println("队列满,不能加入数据");
			return;
					
		}
		
		//rear后移之后把新数据加入即可
		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 headQueue() {
		
		if(isEmpty()) {
			throw new RuntimeException("队列空,没有数据");
		}	
		return arr[front +1];
	}
}

这种实现方式的问题:
使用过后的空间不能再复用,浪费数组的空间

解决方法:
利用数组构建一个环形队列,通过取模的方式来复用数组空间



-------------数组模拟环形队列的思路分析

思路分析:(利用% 改进成一个环形队列)

  1. 正常情况下,如果把数组看成一个环,那么当rear指向数组的最后一个元素时,当要添加一个新的元素,就必须判断数组前面位置里的元素是否已经被取出了。

  2. front变量的含义需要做一个调整:arr[front]指向队列的第一个元素,所以front初始值设为0

  3. rear变量的含义也做一个调整:arr[rear]指向队列的最后一个元素的后一个位置,rear的初始值也设为0 (这个位置在arr中实际上不存在,是一个预留空间) 数组本身的长度就是maxSize,当rear的值为maxSize - 1的时候,再次输入一个数据,就会让rear指针到达实际不存在的区域,通过取模返回到数组的前面的位置。(rear+1) % maxSize 就会指向0,也就是数组的第一个区域。

  4. 当队列满时,条件:(rear + 1)% maxSize == front; 换句话说就是取模后发现rear+1指向的是和数组的第一个数是一个位置,也就说是队列没有空余位置存放取模后的数据

    注意,这里的rear+1是变化的,不一定会指向数组的最后

  5. 当队列为空,条件:rear == front;

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

    简单说明一下:(rear + maxSize - front) % maxSize的算法思路

    这里分为两种情况

    【数据结构笔记】数组模拟队列 ArrayQueue_第2张图片

-----------------数组模拟环形队列的代码实现

Example:

import java.util.Scanner;

public class CircleQuene {
	public static void main(String[] args) {
		
		System.out.println("测试数组模拟环形队列");
		
		//创建一个队列
		//注意,这里设置的4,其队列有效数据最大为3
		//因为有一个空间是作为预留用来取模的
		
		CircleQueue queue = new CircleQueue(4);
		char key = ' '; 
		Scanner scanner = new Scanner(System.in);
		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 = scanner.next().charAt(0);
			
			
			switch(key) {
			case 's':
				queue.showQueue();
				break;
				
			case 'a'://队列可能已经满了,所以用try catch
				System.out.println("请输入要加入队列的数");
				try {
					queue.addQueue(scanner.nextInt());
				}catch(Exception e) {
					System.out.println(e.getMessage());
				}
				break;

				
			case 'g' ://可能里面没有数据,所以用try catch
				try {
					int res = queue.getQueue();
					System.out.println("取出数据:" + res);
				}catch(Exception e) {
					System.out.println(e.getMessage());
				}
				break;
				
			case 'h' ://可能里面没有数据,所以用try catch
				try {
					System.out.println("队列第一个数据为" + queue.HeadQueue());
				}catch(Exception e) {
					System.out.println(e.getMessage());
				}
				break;
				
			case 'e' :
				scanner.close();
				loop = false;
				
				System.out.println("程序退出");
				System.exit(0);
				break;
				
			default:
				break;
		        }
		    }
	}
}

class CircleQueue{
	
	private int maxSize; 
	
	private int front;
	private int rear;	
	private int[] arr; 

	
	
	/*
	 * 构造方法
	 */
	public CircleQueue(int arrMaxSize) {
		this.maxSize = arrMaxSize;
		arr = new int[arrMaxSize];
		front = 0; 
		rear = 0; 

	}
	
	/*
	 * 判断队列是否已满
	 */
	public boolean isFull() {
		return (rear + 1)%maxSize == front;
		
	}
	
	/*
	 * 判断队列是否为空
	 */
	public boolean isEmpty() {
		return rear == front;
	}
	
	/*
	 * 添加数据到队列
	 */
	public void addQueue(int n) {
		
		if(isFull()) {
			throw new RuntimeException("队列满,不能加入");
		}
		
		//直接将数据加入
		arr[rear] = n;
		
		//如果队列不满,让rear后移,
		//如果rear没有到最后,后移一位,取模不会对rear造成影响
		//如果rear已经到了最后,后移一位,取模把rear指向0
		rear = (rear+1)%maxSize;


	}
	
	/*
	 * 从队列取出数据
	 */
	public int getQueue() {
		
		if(isEmpty()) {
			throw new RuntimeException("队列空,不能取出");
		}
		
		//front指向队列的第一个元素,也就是我们要取出的数
		//先把front的值保存到临时变量value,在考虑把front后移
		int value = arr[front];

		//后移front,不过不能直接front++了
		//front也有可能从圆环的另一头过来,要考虑取模		
		front = (front +1) % maxSize;

		//由于front指向的是队列第一个数据,也就是我们储存的value,所以返回value。		
		return value;

	}
	
	/*
	 * 显示队列的所有数据
	 */
	
	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]);
//		}

		
		//思路:从front开始遍历,遍历多少个元素(有效元素的个数)
		for(int i = front; i < front + ((rear -front + maxSize)% maxSize); i++) {
		System.out.printf("arr[%d] = %d\n",i % maxSize, arr[i % maxSize]);
		}
	}
	
	/*
	 * 显示队列的头数据,注意不是取出数据
	 */
	public int HeadQueue() {
		if(isEmpty()) {
			throw new RuntimeException("队列空");
		}
		return arr[front];
		
	}
}

你可能感兴趣的:(数据结构学习笔记)