如果我们测试当前实现的pop()方法,会发现它适用下面的案例:如果向栈内push数据,栈的大小会增加1,如果从栈中pop()数据,栈的大小会减少1!
为了处理这个用例,我们将向pop()中添加if语句。
Stack.prototype.pop = function() { var size = this._size, deletedData; if (size) { deletedData = this._storage[size]; delete this._storage[size]; this._size--; return deletedData; } };
通过添加if语句,可以使代码在存储中有数据时才被执行。
栈的完整实现
我们已经实现了完整的栈结构。不管以怎样的顺序调用任何一个方法,代码都可以工作!下面使代码的最终版本:
function Stack() { this._size = 0; this._storage = {}; } Stack.prototype.push = function(data) { var size = ++this._size; this._storage[size] = data; }; Stack.prototype.pop = function() { var size = this._size, deletedData; if (size) { deletedData = this._storage[size]; delete this._storage[size]; this._size--; return deletedData; } };
从栈到队列
当我们想要按顺序添加数据或删除数据时,可以使用栈结构。根据它的定义,栈可以只删除最近添加的数据。如果想要删除最早的数据该怎么办呢?这时我们希望使用名为queue的数据结构。
队列
与栈类似,队列也是一个线性数据结构。与栈不同的是,队列只删除最先添加的数据。
为了帮助你明白队列是如何工作的,让我们花点时间举个例子。我们可以把队列想象成为熟食店的售票系统。每个顾客拿一张票,当他们的号码被呼叫时接受服务。持第一张票的顾客首先接受服务。
再进一步想象一下,这张票上有一个数字“1”。下一张票上有数字“2”。得到二张票的顾客将会第二个接受服务。(如果我们的售票系统像栈一样运行,最先进入堆栈的客户将会最后一个接受服务!)
队列的一个更实际的例子是Web浏览器的事件循环。当触发不同事件时,例如单击某个按钮,点击事件将被添加到事件循环队列中,并按照它们进入队列的顺序进行处理。
现在我们具有了队列的概念,接下来就要定义它的操作。你会注意到,队列的操作和栈非常相似。区别就在被删除的数据在什么地方。
enqueue(data) 将数据添加到队列中。
dequeue 删除最早加入队列的数据。
队列的实现
现在让我们开始写队列的代码吧!
队列的属性
在实现队列的代码中,我们将会创建一个名为 Queue 的构造方法。接下来添加三个属性:_oldestIndex, _newestIndex, 和 _storage。在下一小节中,_oldestIndex 和 _newestIndex 的作用将变得更加清晰。
function Queue() { this._oldestIndex = 1; this._newestIndex = 1; this._storage = {}; }
队列的方法
现在我们将创建队列会用到的三个方法:size(), enqueue(data), 和 dequeue(data)。我将描述每个方法的作用,写出每个方法的代码,然后解释这些代码。
方法1/3:size( )
这个方法有两个作用:
返回当前队列的长度。
保持队列中键的正确范围。
Queue.prototype.size = function() { return this._newestIndex - this._oldestIndex; };
实现 size() 可能显得微不足道,但你会很快发现并不是这样的。为了理解其原因,我们必须快速重新审视 size() 在栈结构中的实现。
回想一下栈的概念模型,假设我们把5个盘子添加到一个栈上。栈的大小是5,每个盘子都有一个数字,从1(第一个添加的盘子)到5(最后一个添加的盘子)。如果我们取走三个盘子,就只剩下两个盘子。我们可以简单地用5减去3,得到正确的大小,也就是2。这是关于栈大小最重要的一点:当前大小相当于从栈顶部的盘子(2)到栈中其他盘子(1)的计数。换句话说,键的范围总是从当前大小到1之间。
现在,让我们将栈大小的实现应用到队列中。假设有五个顾客从我们的售票系统中取到了票。第一个顾客有一张显示数字1的票,第五个客户有一张显示数字5的票。现在有了一个队列,拿着第一张票的第一位顾客。
假设第一个客户接受了服务,这张票会从队列中被移除。与栈类似,我们可以通过从5减去1来获得队列的正确大小。那么服务队列中还有4张票。现在出现了一个问题:队列的大小不能对应正确的票号。如果我们从五减去一个,得到大小是4,但是不能使用4来确定当前队列中剩余票的编号范围。我们并不能确定队列中票号的顺序到底是1到4还是2到5。