今天但看了大二数据结构这本书,对队列进行一个整理。
文章目录
- 一、什么是队列
- 二、顺序数组实现队列
- 三、循环数组实现队列
- 四、链表实现队列
一、什么是队列
队列和栈一样,都是一种受限制的线性表。队列元素只能从队尾插入(称为入队),队首删除(称为出队),就像排队买奶茶,作为一名有素质的中国人,新来的人会自动地排在队伍的后面,队伍前面的人会先买到奶茶。这就是所谓的先进先出(First In First Out)。队列实现的方式有两种基本结构:数组和链表。我们想要实现队列的基本操作包括:
//入队
//出队
//获取队首元素
//获取长度
//是否为空
//是否为满
//显示队列元素
二、顺序数组实现队列
假设有这样一个队列,队首处于数组下标为0的位置,队尾在数组后端,那么显然入队的时候就是在队尾添加元素,时间复杂度为O(1),但是出队的时候要删除Array[0]的元素,数组后面的元素需要全部往前移动,时间复杂度为O(n),显然,如果把队尾放在顺序数组的位置0处,那么出队的时间复杂度为O(1),入队的时间复杂度为O(n),如图所示:
针对上述的问题,我们可以添加两个指针front(队首),rear(队尾),在入队和出队的操作只需要移动两个指针的位置,而无需移动队列中的元素,将时间复杂度都降为O(1)。
你以为这样就完事了吗?当然不是,数组一个固有的特点,就是一旦创建之后数组的大小就固定不变了,如果我们不断地进行入队和出队操作,就会出现一系列问题,队列满了,数组越界了!随着front的值不断增加,数组前面的一大片空间就浪费了,内存是不是要哭死,哈哈!所以下面就会讲述关于循环数组实现队列的方法来处理上述的弊端。
三、循环数组实现队列
使用网上优秀的图,说明一下判断队列空和满的方法。
代码实现:
public CircleQueue() {
maxSize = 4;
front = 0;
rear = 0;
dataArr =(E[])new Object[maxSize];
}
//入队
public void enqueue(Object data) {
if(isFull()) {
System.out.println("队列满了!!!");
}else {
dataArr[rear] = data;
rear = (rear+1)%maxSize;
}
}
//出队
public Object dequeue() {
if(isEmpty()) {
System.out.println("队列空了!!!");
return null;
}else {
Object o = dataArr[front];
front = (front+1)%maxSize;
return o;
}
}
//获取队列长度
public int length() {
return (rear-front+maxSize)%maxSize;
}
//判断是否为空
public boolean isEmpty() {
return rear == front;
}
//判断是否为满
public boolean isFull() {
return (rear+1)%maxSize == front;
}
public void print() {
if(length()==0) {
System.out.println("队列元素为空");
}else {
System.out.println("队列元素为:");
for(int i=front;i<=length();i++) {
if(dataArr[i] != null) {
System.out.println(dataArr[i]);
}
}
}
System.out.println("rear="+rear);
System.out.println("front="+front);
}
测试代码:
public static void main(String[] args) {
CircleQueue<Object> queue = new CircleQueue<>();
queue.enqueue(2);
queue.enqueue(3);
queue.enqueue(4);
System.out.println("队列长度= "+queue.length());
queue.print();
queue.enqueue(5);
System.out.println("队列长度= "+queue.length());
queue.print();
queue.dequeue();
queue.print();
}
测试结果:
四、链表实现队列
利用链表实现队列,可以动态地创建结点,不用预设大小,也不会造成空间的浪费,整个队列的内存空间不连续,在实现插入和删除操作更容易实现,但是存取速度会慢。链式队列的实现就是对链表做了简单的修改,根据队列的特点,我们可以选用双端链表的形式,创建两个指针front,rear指向链表头结点和尾结点。
同样实现上述的基本操作。
package com.lm.MyQueue1121;
public class QueueLinklist {
private Node front;
private Node rear;
public class Node{
private Node next;
private Object data;
public Node(Object data) {
this.data = data;
}
}
public QueueLinklist() {
front = null;
rear = null;
}
//入队
public void enqueue(Object value) {
Node node = new Node(value);
if(isEmpty()) {//注意插入第一个节点,要给front赋值
rear = node;
front = node;
}else {
rear.next = node;
rear = node;
}
}
//出队
public Object dequeue() {
if(isEmpty()) {
System.out.println("队列为空!!!");
return null;
}else {
Object o = front.data;
front = front.next;
System.out.println(o+" 出队列");
return o;
}
}
//获取长度
public int length() {
Node node = front;
int len = 0;
while(node != null) {
len++;
node = node.next;
}
return len;
}
//判断是否为空
public boolean isEmpty() {
return length()==0;
}
public void print() {
Node node = front;
if(node == null) {
System.out.println("队列为空!!!");
}
while(node != null) {
System.out.println("队列元素:"+node.data);
node = node.next;
}
}
public static void main(String[] args) {
QueueLinklist queue = new QueueLinklist();
queue.enqueue(1);
queue.enqueue(2);
queue.enqueue(3);
queue.print();
System.out.println("队列长度="+queue.length());
queue.dequeue();
queue.dequeue();
queue.print();
System.out.println("队列长度="+queue.length());
}
}
测试结果: