一、队列
队列是一个【有序】列表,可以用【数组】或是【链表】来实现。
如果是用数组实现的,就是顺序存储:地址值是连续的。
如果使用链表实现的,就是链式存储:地址值不一定是连续的。
二、数组实现队列
队列结构包含三个变量:front(头部)、rear(尾部)、maxSize(队列最大容量)
2.1 普通队列
初始值:
rear = front = -1
列满:rear == maxSize - 1
列空:rear == front
出队列:
1.front++
2.return arr[front]
入队列:
1.rear++
2.arr[rear] = data
普通队列的缺点:
数组只能使用一次,即队列中存满数据,并且取出一个数据后,无法继续添加数据了。
简单理解,就是不能复用。
2.2 环形队列
环形队列可以解决普通队列无法复用的问题。
环形就是把数组看成一个圈,如果想实现这种首尾相连的效果,可以通过取模运算来实现。
核心思想:
1)front指向队列的【第一个】元素
2)rear是指向队列的最后一个元素的【后一个】位置
3)初始值:rear = front = 0
4)思想分析
我们希望空出一个空间,这个空间用来判断列满还是列空。
即当maxSize=4时,从空开始添加数据,arr[3]不存储数据,而是用来判断队列是否已经满了。
当第一次队列满了以后,从队列取出一个元素,若继续添加数据,则是放在arr[3]的位置,此时arr[1]不存储数据,而是用来判断。
队列满:(rear + 1) % maxSize == front
队列空:rear == front
有效数据个数:(rear + maxSize - front) % maxSize
三、链表实现队列
使用单向链表实现队列,因为要满足队列【先入先出】的的规则,所以在每次添加节点的时候,插入到链表【最后端】即可。
1)队列满:有效节点个数 == maxSize
2)队列空:head.getNext() == null
3)入队列:新节点添加到单向链表尾部
4)出队列:取出单向链表的最前端节点
四、Java代码实现
1.使用数组实现普通队列
public class ArrayQueue {
private int maxSize; //数组最大容量
private int front; //队列头
private int rear; //队列尾
private int[] arr; //该数组用于存放数据,这样写在每次新建对象时会减少引用次数,直接赋值即可。
//创建队列的构造器
public ArrayQueue(int maxSize){
this.maxSize = maxSize;
this.arr = new int[maxSize];
this.front = -1; //指向队列头部的【前一个】位置
this.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;
}else {
rear++; //让rear后移一位
arr[rear] = n;
}
}
//获取队列的数据,即出队列
public int getQueue(){
//首先判断队列是否为空
if (isEmpty()){
//通过抛出异常来处理
/*
throw本身就会停止代码,所以不需要写return
return有两个作用:
1)返回数据到调用处
2)结束代码
*/
throw new RuntimeException("队列为空,不能获取数据!");
}else {
front++; //front后移一位
return arr[front];
}
}
//显示队列的所有数据
public void showQueue(){
if (isEmpty()){
System.out.println("队列为空,没有数据!");
}else {
for (int i : arr) {
System.out.println(i);
}
}
}
//显示队列的头数据,注意不是取出数据
public int headQueue(){
//判断是否为空
if (isEmpty()){
throw new RuntimeException("队列为空,没有数据!");
}else {
return arr[front + 1];
}
}
}
2.使用数组实现环形队列
public class CircleArrayQueue {
private int maxSize; //数组最大容量
private int front; //队列头.默认值为0
private int rear; //队列尾.默认值为0
private int[] arr; //该数组用于存放数据,即模拟队列
//构造器
public CircleArrayQueue(int maxSize){
this.maxSize = maxSize;
this.front = 0;
this.rear = 0;
this.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;
}else {
//因为rear本身就是指向最后一个元素的后一个位置,所以直接将数据加入即可
arr[rear] = n;
//将rear后移一位,但是【必须要考虑取模】
rear = (rear + 1) % maxSize;
}
}
//从队列中取出数据
public int getQueue(){
//判断队列是否为空
if (isEmpty()){
throw new RuntimeException("队列为空,不能获取数据!");
}else {
//front是指向队列的第一个元素
//1.先把front对应的值保留到一个临时变量
//2.将front后移,因为是环形队列,所以【考虑取模】
//3.将临时保存的变量返回
int value = arr[front];
front =(front + 1) % maxSize;
return value;
}
}
//显示队列所有数据
public void showQueue(){
if (isEmpty()){
System.out.println("队列为空,没有数据!");
return;
}else {
//思路:从front开始遍历,遍历多少个元素
for (int i = front; i < front + size(); i++){
System.out.printf("arr[%d]=%d\n", i % maxSize, arr[i % maxSize]);
}
}
}
//求出当前队列有效数据个数
public int size(){
return (rear + maxSize - front) % maxSize;
}
//查看队列头数据
public int headQueue(){
if (isEmpty()){
throw new RuntimeException("队列为空,没有数据!");
}else {
return arr[front];
}
}
}
3.使用单向链表实现队列
使用单向链表实现的队列是可以复用的。
public class LinkedListQueue {
private int maxSize; //队列的最大容量
private Node head = new Node(-1, -1); //初始化头节点
/**
* 构造方法
* @param maxSize 队列的最大容量
*/
public LinkedListQueue(int maxSize) {
this.maxSize = maxSize;
}
/**
* 判断队列是否满
* @return
*/
public boolean isFull(){
return getLength() == maxSize;
}
/**
* 判断队列是否为空
* @return
*/
public boolean isEmpty(){
boolean b = false;
try {
b = head.getNext() == null;
}catch (Exception e){
System.out.println(e.getMessage());
}
return b;
}
/**
* 入队列
* @param newNode
*/
public void add(Node newNode){
if (isFull()){
System.out.println("栈满!");
}else {
Node temp = head;
while (true){
if (temp.getNext() == null){
break;
}else {
temp = temp.getNext();
}
}
temp.setNext(newNode);
}
}
/**
* 出队列
* @return
*/
public Node get(){
if (isEmpty()){
throw new RuntimeException("队列为空!");
}
Node temp = head.getNext();
head.setNext(temp.getNext());
return temp;
}
/**
* 获取链表的有效节点个数
* @return
*/
public int getLength(){
if (head.getNext() == null){
return 0;
}else {
Node temp = head.getNext();
int length = 0;
while (temp != null){
length++;
temp = temp.getNext();
}
return length;
}
}
public Node first(){
if (isEmpty()){
throw new RuntimeException("队列为空!");
}
return head.getNext();
}
/**
* 显示当前队列中的数据
*/
public void show(){
if (isEmpty()){
System.out.println("队列为空!");
}
Node temp = head.getNext();
while (temp != null){
System.out.println(temp);
temp = temp.getNext();
}
}
}
其中Node类的定义为:
public class Node {
private int node; //节点编号
private int value; //节点的数据
private Node next; //节点的next
public Node(int node, int value) {
this.node = node;
this.value = value;
}
public int getNode() {
return node;
}
public void setNode(int node) {
this.node = node;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
@Override
public String toString() {
return "Node{" +
"node=" + node +
", value=" + value +
'}';
}
}