目录
学习资料:b站韩顺平数据结构,参考笔记数据结构与算法 系列教程(笔记)仅自我学习总结记录用。
1)构造器:【传入的是有效元素个数,数组的长度是有效元素个数+1】
3)判断是否为满【(rear+1)%maxSize == front】
4)入队列【先存入值,再后移rear(后移要注意越界问题,取模)】
5)出队列【先取出值,再后移front,(后移要注意越界问题,取模)】
6)显示队列中的数据【从队首遍历,遍历次数为有效元素个数次,但索引越界了需要取模】
8)显示尾部数据【取数据rear-1,但是可能rear为0,所以要条件表达式判断,为0队尾数据则为maxSize-1,最后一个数据】
学习资料:b站韩顺平数据结构,参考笔记数据结构与算法 系列教程(笔记)仅自我学习总结记录用。
一、队列:
队列是一个有序列表,可以用 数组 或链表实现。
特点:先入先出。
二、数组模拟队列
1、声明四个变量:
arr:用来存储数据的数组。
maxSize:该队列的最大容量。
front:队列头,指向队列头的前一个位置。
rear:队列尾,指向队列尾的最后一个有效数据元素。
2、声明的方法:
(构造器)
判断是否满 —— rear = maxSize - 1 (满)
判断是否空 —— rear = front (空)
入队列 —— 判断是否满,未满,rear后移,入队列
出队列 —— 判断是否空,未空,出队列,front后移
显示队列数据 —— 判断是否空
显示头部数据 —— 判断是否空
显示尾部数据 —— 判断是否空
3、各部分代码:
1)构造器:
2)判断是否为空
3)判断是否为满
4)入队列
5)出队列
6)显示队列中的数据
7)显示头部数据
8)显示尾部数据
总体代码:
package com.guigu.Queue;
//队列 特点:先入先出,先存入的数据先取出。
//数组模拟队列。
//arr 存储数据的数组
//front 队首,队列头部
//rear 队尾,队列尾部
//maxSize 队列的最大容量
//思路分析: add 向队列中添加数据
// 将尾指针往后移 ————> 判断队列是否为空(front == rear) :rear + 1
// 存入数据 ————> 判断队列是否满(rear < maxSize - 1): 未满则将数据存入rear所指的数组元素中,否则无法存入数据。
//
public class ArrayQueueDemo {
public static void main(String[] args) {
ArrayQueue queue = new ArrayQueue(3);
//测试:入队列
queue.add(1);
queue.add(2);
queue.add(3);
System.out.println("1、测试:查看队列中的数据");
queue.show();
System.out.println("2、测试:查看队列头数据:" + queue.head());
System.out.println("3、测试:查看队列尾数据:" + queue.tail());
//测试添加数据
queue.add(4);
System.out.println("4、测试:(出队列)获取队列数据:" + queue.get());
System.out.println("5、测试:查看队列中的数据");
queue.show();
}
}
class ArrayQueue{
private int maxSize;//队列的最大容量,数组的索引范围是[0,maxSize - 1]。
private int front;//队列头,指向队列头的前一个位置
private int rear;//队列尾,指向队列尾的最后一个有效数据元素
private int arr[];//用于存储数据,模拟队列
//构造器
public ArrayQueue(int arrMaxSize){//数组最大的值
maxSize = arrMaxSize;
arr = new int[maxSize];
front = -1;
rear = -1;
}
//判断队列是否为空,判断队尾是否等于队首,是则为空
public boolean isEmpty(){
return rear == front;
}
//判断队列是否为满,rear = maxSize - 1,数组被完全使用,无法再存储更多数据。
public boolean isFull(){
return rear == maxSize - 1;
}
//出队列,先判断是否为空,空则抛出异常。先进先出,front后移。
public int get(){
if(isEmpty()){
throw new RuntimeException("队列空");
}
return arr[++front];
}
//入队列,先判断是否为满,满则退出。入队列rear后移再赋值。
public void add(int n){
if(isFull()){
System.out.println("队列已满");
return;
}
rear++;
arr[rear] = n;
}
//展示队列中的数据————遍历数组。判断队列是否为空,空无法展示
public void show(){
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指向队列头前一个元素
public int head(){
if(isEmpty()){
throw new RuntimeException("队列空");
}
return arr[front+1];
}
//查看队列尾数据
public int tail(){
if(isEmpty()){
throw new RuntimeException("队列空");
}
return arr[rear];
}
}
结果:
三、环形队列
上面的普通数列,是一次性的,不可以复用。
环形数组需要注意!!!!!
rear和front的定义,队首队尾的概念
有效元素个数的计算
越界问题(左:rear-1可能是-1,右:rear或front+1可能超过了数组的maxSize)
遍历时,从队首开始,遍历有效元素个数次。注意越界问题。
1、声明四个变量:
arr:用来存储数据的数组。
maxSize:该队列的最大容量。
front:队列头,指向队列头。
rear:队列尾,指向队列尾有效数据元素的下一个位置。【rear的指向一直是空的】
2、声明的方法:
(构造器)
判断是否满 —— (rear + 1) % maxSize = front (满)
判断是否空 —— rear = front (空)
有效元素个数,公式:(rear - front +maxSize) % maxSize
入队列 —— 判断是否满,未满,入队列,rear后移【注意越界问题,取模】
出队列 —— 判断是否空,未空,出队列,front后移【注意越界问题,取模】
显示队列数据 —— 判断是否空
显示头部数据 —— 判断是否空
显示尾部数据 —— 判断是否空【注意尾部】
解释1:有效个数 (rear + maxSize - front) % maxSize
解释2:为什么rear一直指向空位置。
解释3:取模。
取模运算是为了处理环形队列的跨越边界问题。
3、各部分代码:
1)构造器:【传入的是有效元素个数,数组的长度是有效元素个数+1】
传入是有效数据个数。
2)判断是否为空【front==rear】
3)判断是否为满【(rear+1)%maxSize == front】
4)入队列【先存入值,再后移rear(后移要注意越界问题,取模)】
5)出队列【先取出值,再后移front,(后移要注意越界问题,取模)】
6)显示队列中的数据【从队首遍历,遍历次数为有效元素个数次,但索引越界了需要取模】
7)显示头部数据【最简单,队首数据】
8)显示尾部数据【取数据rear-1,但是可能rear为0,所以要条件表达式判断,为0队尾数据则为maxSize-1,最后一个数据】
总体代码:jie
package com.guigu.Queue;
//环形队列
//数组模拟队列的基础版,当填满队列之后就添加不进去了,获取数据也不能清空原队列中的数据。
//优化: 将数组改进成一个环形队列。
//front : 队列的第一个元素。(有所区别)。初始值为0
//rear : 队列的最后一个元素的下一个位置。(有所区别)。初始值为0
//队列满 : (rear + 1) % maxSize == front
//队列空 : rear == front
//队列中有效元素个数计算公式 : (rear + maxSize - front) % maxSize
public class CircleQueueDemo {
public static void main(String[] args) {
CircleQueue queue1 = new CircleQueue(3);//传入有效数据,3个,实际数组长度0-3(4)
//测试:入队列
queue1.add(1);
queue1.add(2);
queue1.add(3);
System.out.println("1、测试:查看队列中的数据");
queue1.show();
System.out.println("2、测试:查看队列头数据:" + queue1.head());
System.out.println("3、测试:查看队列尾数据:" + queue1.tail());
//测试添加数据
//queue1.add(4);
System.out.println("4、测试:(出队列)获取队列数据:" + queue1.get());
System.out.println("4、测试:(出队列)获取队列数据:" + queue1.get());
queue1.show();
queue1.add(4);
System.out.println("5、测试:查看队列中的数据");
queue1.show();
}
}
class CircleQueue{
private int maxSize;//队列最大容量
private int front;//队列头,队头的元素
private int rear;//队列尾,队尾的下一个元素,rear指向的位置一直是空的。
private int arr[];//用于存储数据,模拟队列
public CircleQueue(int arrMaxSize){
maxSize = arrMaxSize + 1;//arrMaxSize有效数据个数,maxSize是数组长度。
arr = new int[maxSize];
front = 0;
rear = 0;
}
//判断队列是否满,
//front是队头元素的位置,rear是指向有效元素的下一个位置,如果rear不是指向一个空元素,那么无法区分 队列满和队列空
public boolean isFull(){
return (rear + 1) % maxSize == front;//用取模实现循环
}
//判断队列是否空,
public boolean isEmpty(){
return front == rear;
}
//有效个数,有效元素的数量是从 front 到 rear 的距离。
public int size(){
return (rear + maxSize - front) % maxSize;
}
//入队列,先判断是否为满,满则退出。入队列rear赋值再后移(后移注意越界问题!)。
public void add(int n){
if(isFull()){
System.out.println("队列已满");
return;
}
//arr[rear++] = n;错误,可能越界
arr[rear] = n;
rear = (rear + 1) % maxSize;
}
//出队列,先判断是否为空,空则抛出异常。先进先出,先出再front后移(后移注意越界问题)。
public int get(){
if(isEmpty()){
throw new RuntimeException("队列空");
}
int value = arr[front];
front = (front + 1) % maxSize;
return value;
}
//展示队列中的数据————遍历数组。
// 需要从队首开始打印,
// 打印的次数尾有效元素个数,
// 获取数据的下标,由于是环形的,需要使用取模的方式来获取
public void show(){
if(isEmpty()){
System.out.println("队列为空");
return;
}
for (int i = front; i < front + size(); i++){
//size() 是有效元素的个数,而不是绝对数组下标的范围;
//i 是逻辑上的数组下标,它应该以 i < front + size() 为条件。
int index = i % maxSize;
//遍历时需要用 i % maxSize 取模来确保数组下标始终在有效范围 [0, maxSize - 1] 内;
System.out.printf("arr[%d] = %d \n",index,arr[index]);
}
}
//查看队列的头部数据。front指向队列头
public int head(){
if(isEmpty()){
throw new RuntimeException("队列空");
}
return arr[front];
}
//查看队列尾数据
public int tail(){
if(isEmpty()){
throw new RuntimeException("队列空");
}
//错误:return arr[rear - 1];//当环形时,rear = 0
return rear - 1 < 0 ? arr[maxSize - 1] : arr [rear - 1];
}
}
结果: