前言:
栈和队列很相似,上一篇插曲简单比较了线性表的顺序存储结构和链式存储结构,接下来继续回到正题,数据结构之队列。
1.what is queue(队列是啥啊?)
摘自《数据结构》:队列(queue)是一种先进先出(first in first out,缩写为FIFO)的线性表,它只允许在表的一端进行插入,而在另一端删除元素。如同排队,最先到的最先离开。
同样队列也有两种存储方式:链式存储和顺序存储。
1.1先说链式队列:
队列的两种状态:
- 当队列为空时,头指针(front)和尾指针(rear)指向同一基地址。如图a所示
- 当队列不为空时,头指针和尾指针分别指向队列的开始和队列的最后一个元素,阴影部分是data,白色部分是地址,尾指针的地址是Null。如图b所示
那如何插入元素到队列中呢,和出队列呢?x入队列,则将原来的尾指针指向的结点的地址指向x的地址,再将尾指针(rear)指向x,尾结点的地址为null。同理再插入y。如下图a所示。
那么出队列呢,按照先进先出的原则,出队列从头部出,那就让头指针(front)指向下一个结点。此时就out啦!如下图b所示
ps:由于javascript的解释器针对数组都做了直接的优化,不会存在在很多编程语言中数组固定长度的问题(当数组填满后再添加就比较困难了,包括添加删除,都是需要把数组中所有的元素全部都变换位置的,javascript的的数组确实直接给优化好了,如push,pop,shift,unshift,split方法等等…) 所以JavaScript中常用的栈和队列都是基于数组的简单的顺序存储结构。如果要使用js来模拟链表结构 请看这篇博客
所以在此 就暂时不用链式结构来模拟链式队列了。
1.2 再说循环队列——队列的顺序表示和实现
在队列的顺序存储结构中,除了用一组地址连续的存储单元依次存放从队列到队尾的元素之外,还要加两个指针,分别指向队列的头元素和队列尾元素的位置。如下图a所示
我们分别来做几件事:
1. 第一件事如图b所示让五个元素入队列,此时front指向a,rear指向e。
2.第二件事 如图c所示 出队列一次,队列的头指针指向下个元素 此时front指向b
3.第三件事 如图d所示 再出队列3次,此时头指针指向e,尾指针也指向e.
4.假如 此时我再让元素f入队列,会发生什么。。。。。。。。。会发生错误,不能再让元素入队列了,因为存储单元给定是5个,再加入就越界了。
可是,明明下面还有4个空的存储单元可以被利用。此种使用方式 堪称实在是奢侈。那么怎样节约资源呢??
循环队列诞生啦!循环队列就是将顺序队列臆造为一个环状的空间。如图所示:
当头指针向下个元素移动时,留出的空间,可以继续入队列。解决了奢侈问题,但是否会引入其他问题呢?
先看它如何来做事:
1.如下图a所示,队列头元素时J3,尾元素是J5
2.如下图b所示,相继插入J6、J7、J8,此时队列空间被占满。此时Q.rear = Q.front;
PS:为什么队尾指针指向的是空元素?这是要看事先约定的。一般有两种方案:方案一、将队首指针指向实际队首,队尾指针指向队尾的下一个空位;方案二、将队首指针指向队首前一个空位,队尾指针指向实际队尾。这里用的是方案一。
那么问题来了... 当队列为空和队列为满的时候始终有Q.rear = Q.front; 如果仅凭 Q.rear = Q.front 是不能判断该队列是“空”还是“满”;
如何解决这个问题呢? 人为约束
1.可以约定另设一个标志位以区别队列是空还是满
2.少用一个元素的空间。
2.js模拟循环队列
循环队列是顺序结构,我们就用顺组来模拟。这里采用的是少用一个元素的空间来避免无法判断队列是空还是满的问题。
功能表
功能 | 功能描述 |
length | 返回队列长度 |
inQueue | 入队列 |
outQueue | 出队列 |
clearQueue | 清空队列 |
isEmpty | 判断队列是否为空 |
function circleQueue(size){ var cirQueue = []; this.size = size; this.maxsize = size + 1; this.front = 0; this.rear = 0; this.inQueue = function(element){ if (typeof element == "undefined" || element = ""){ return; } var pos = (this.rear +1) % this.maxsize; if(pos == this.front){ // 队列满 return; }else { this.cirQueue[this.rear] = element; this.rear = pos; } }; this.outQueue = function(){ if(this.rear == this.front){ // 判断队列是否为空 return; }else { var element = this.cirQueue[this.front]; this.front = (this.front - 1)% this.maxsize; return element; } }; this.length = function(){ return (this.rear - this.front + this.maxsize) % this.maxsize; }; this.clearQueue = function(){ this.rear = 0; } this.isEmpty = function(){ return this.rear == this.front; } }