JavaScript数据结构——队列的实现与应用
<p> 队列与栈不同,它遵从先进先出(FIFO——First In First Out)原则,新添加的元素排在队列的尾部,元素只能从队列头部移除。</p><p> 我们在前一篇文章中描述了如何用JavaScript来实现栈这种数据结构,这里我们对应地来实现队列。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> Queue() {
let items </span>=<span style="color: rgba(0, 0, 0, 1)"> [];
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 向队列添加元素(一个或多个)</span>
<span style="color: rgba(0, 0, 255, 1)">this</span>.enqueue = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (element) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (element <span style="color: rgba(0, 0, 255, 1)">instanceof</span> Array) items =<span style="color: rgba(0, 0, 0, 1)"> items.concat(element);
</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> items.push(element);
};
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 从队列移除元素</span>
<span style="color: rgba(0, 0, 255, 1)">this</span>.dequeue = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> () {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> items.shift();
};
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回队列中的第一个元素</span>
<span style="color: rgba(0, 0, 255, 1)">this</span>.front = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> () {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> items;
};
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 判断队列是否为空</span>
<span style="color: rgba(0, 0, 255, 1)">this</span>.isEmpty = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> () {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> items.length === 0<span style="color: rgba(0, 0, 0, 1)">;
};
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回队列的长度</span>
<span style="color: rgba(0, 0, 255, 1)">this</span>.size = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> () {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> items.length;
};
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 清空队列</span>
<span style="color: rgba(0, 0, 255, 1)">this</span>.clear = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> () {
items </span>=<span style="color: rgba(0, 0, 0, 1)"> [];
};
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 打印队列内的所有元素</span>
<span style="color: rgba(0, 0, 255, 1)">this</span>.print = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> () {
console.log(items.toString());
};
}</span></pre>
</div>
<p> 与栈的实现方式类似,唯一不同的是从队列移除元素时取的是队列头部的元素(最先添加的),而栈则是取的顶部元素(最后添加的)。下面是一些测试用例及返回结果:</p>
<div class="cnblogs_code">
<pre>let queue = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Queue();
console.log(queue.isEmpty()); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> true</span>
<span style="color: rgba(0, 0, 0, 1)">
queue.enqueue(</span>'John'<span style="color: rgba(0, 0, 0, 1)">);
queue.enqueue([</span>'Jack', 'Camila'<span style="color: rgba(0, 0, 0, 1)">]);
queue.print(); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> John,Jack,Camila</span>
console.log(queue.size()); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 3</span>
console.log(queue.isEmpty()); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> false</span>
console.log(queue.front()); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> John</span>
<span style="color: rgba(0, 0, 0, 1)">
console.log(queue.dequeue()); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> John</span>
queue.print(); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Jack,Camila</span>
<span style="color: rgba(0, 0, 0, 1)">
queue.clear();
queue.print(); </span><span style="color: rgba(0, 128, 0, 1)">//</span> </pre>
</div>
<p> 注意,我们允许批量向队列中添加元素,为此我们需要判断enqueue方法的参数类型,如果参数是数组,则用concat()函数连接两个数组,如果参数不是数组,则直接用push()函数将元素添加到队列中。</p>
<p> 与栈的实现方式一样,这里我们也同样给出用ES6的WeakMap类来实现的队列版本。</p>
<div class="cnblogs_code">
<pre>let Queue = (<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> () {
const items </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> WeakMap();
class Queue {
constructor() {
items.set(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">, []);
}
enqueue (element) {
let q </span>= items.get(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (element <span style="color: rgba(0, 0, 255, 1)">instanceof</span> Array) items.set(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">, q.concat(element));
</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> q.push(element);
};
dequeue () {
let q </span>= items.get(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> q.shift();
};
front () {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> items.get(<span style="color: rgba(0, 0, 255, 1)">this</span>);
};
isEmpty () {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> items.get(<span style="color: rgba(0, 0, 255, 1)">this</span>).length === 0<span style="color: rgba(0, 0, 0, 1)">;
};
size () {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> items.get(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">).length;
};
clear () {
items.set(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">, []);
};
print () {
console.log(items.get(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">).toString());
};
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> Queue;
})();</span></pre>
</div>
<p> 这两个版本的执行结果是一样的,它们的区别我们在前一篇文章中已经提及过了,这里不再赘述。</p>
<h3>优先队列</h3>
<p> 所谓优先队列,顾名思义,就是说插入到队列中的元素可以根据优先级设置先后顺序。优先级越高位置越靠前,优先级越低位置越靠后。假设优先级用数字来表示,如果数字越小表示的优先级越高,形成的队列就称之为最小优先队列,反之则称之为最大优先队列。下面是实现的代码:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> PriorityQueue() {
let items </span>=<span style="color: rgba(0, 0, 0, 1)"> [];
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 向队列添加元素(一个或多个)</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 参数obj的数据格式:{element, priority}</span>
<span style="color: rgba(0, 0, 255, 1)">this</span>.enqueue = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (obj) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (obj <span style="color: rgba(0, 0, 255, 1)">instanceof</span><span style="color: rgba(0, 0, 0, 1)"> Array) {
</span><span style="color: rgba(0, 0, 255, 1)">for</span> (let i = 0, ci; ci = obj; i++<span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.enqueue(ci);
}
}
</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
let added </span>= <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">for</span> (let i = 0, ci; ci = items; i++<span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 最小优先级,即将priority值小的元素插入到队列的前面</span>
<span style="color: rgba(0, 0, 255, 1)">if</span> (obj.priority <<span style="color: rgba(0, 0, 0, 1)"> ci.priority) {
items.splice(i, </span>0<span style="color: rgba(0, 0, 0, 1)">, obj);
added </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">;
}
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果元素没有插入到队列中,则默认加到队列的尾部</span>
<span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 0, 1)">added) items.push(obj);
}
};
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 从队列移除元素</span>
<span style="color: rgba(0, 0, 255, 1)">this</span>.dequeue = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> () {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> items.shift();
};
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回队列中的第一个元素</span>
<span style="color: rgba(0, 0, 255, 1)">this</span>.front = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> () {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> items;
};
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 判断队列是否为空</span>
<span style="color: rgba(0, 0, 255, 1)">this</span>.isEmpty = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> () {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> items.length === 0<span style="color: rgba(0, 0, 0, 1)">;
};
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回队列的长度</span>
<span style="color: rgba(0, 0, 255, 1)">this</span>.size = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> () {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> items.length;
};
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 清空队列</span>
<span style="color: rgba(0, 0, 255, 1)">this</span>.clear = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> () {
items </span>=<span style="color: rgba(0, 0, 0, 1)"> [];
};
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 打印队列内的所有元素</span>
<span style="color: rgba(0, 0, 255, 1)">this</span>.print = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> () {
items.forEach(</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (item) {
console.log(`${item.element} </span>-<span style="color: rgba(0, 0, 0, 1)"> ${item.priority}`);
});
};
}</span></pre>
</div>
<p> 可以看到,唯一有区别的只有enqueue方法。我们规定所有添加到优先队列的元素都必须满足{element, priority}这种JSON格式,以保证队列中的每一个元素都有一个priority属性来表示优先级。如果要添加的元素的优先级和队列中已有元素的优先级相同,仍然遵循队列的先进先出原则。如果队列中所有元素的优先级比要添加的元素的优先级都高,则将元素添加到队列的末尾。我们将print()方法也做了一些调整,以方便查看输出结果。</p>
<div class="cnblogs_code">
<pre>let queue = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> PriorityQueue();
console.log(queue.isEmpty()); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> true</span>
<span style="color: rgba(0, 0, 0, 1)">
queue.enqueue({element: </span>'John', priority: 2<span style="color: rgba(0, 0, 0, 1)">});
queue.enqueue([{element: </span>'Jack', priority: 1}, {element: 'Camila', priority: 1<span style="color: rgba(0, 0, 0, 1)">}]);
queue.print(); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Jack,Camila,John</span></pre>
</div>
<p> 由于John的优先级比其它两个低,所以它被排在了最后面。虽然Jack和Camila的优先级相同,但是Jack是在Camila之前先插入到队列中的,所以Jack排在了Camila之前,这也符合了我们的预期。</p>
<h3>循环队列</h3>
<p> 我们用一个小游戏“击鼓传花”来说明循环队列在实际中的应用。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> hotPotato(nameList, num) {
let queue </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Queue();
</span><span style="color: rgba(0, 0, 255, 1)">for</span> (let i = 0, ci; ci = nameList; i++<span style="color: rgba(0, 0, 0, 1)">) {
queue.enqueue(ci);
}
let eliminated </span>= ''<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">while</span>(queue.size() > 1<span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 0, 255, 1)">for</span> (let i = 0; i < num; i ++<span style="color: rgba(0, 0, 0, 1)">) {
queue.enqueue(queue.dequeue());
}
eliminated </span>=<span style="color: rgba(0, 0, 0, 1)"> queue.dequeue();
console.log(`${eliminated} has been eliminated.`);
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> queue.dequeue();
}
let names </span>= ['John', 'Jack', 'Camila', 'Ingrid', "Carl"<span style="color: rgba(0, 0, 0, 1)">];
let winner </span>= hotPotato(names, 7<span style="color: rgba(0, 0, 0, 1)">);
console.log(`The winner is: ${winner}`);</span></pre>
</div>
<p> 在这个游戏中,我们传入由五个名字组成的数组,用来表示参加游戏的五个人,数字7表示每一轮要传递的次数。在每一个过程中,我们从队列头部取出一个元素加到队列的尾部,当次数用完的时候,将队列头部的元素取出来,作为这一轮中被淘汰的人。让我们来看一下具体的执行过程,一开始队列中的顺序是John, Jack, Camila, Ingrid, Carl,然后传递7次:</p>
<p> 1. Jack, Camila, Ingrid, Carl, John</p>
<p> 2. Camila, Ingrid, Carl, John, Jack</p>
<p> 3. Ingrid, Carl, John, Jack, Camila</p>
<p> 4. Carl, John, Jack, Camila, Ingrid</p>
<p> 5. John, Jack, Camila, Ingrid, Carl</p>
<p> 6. Jack, Camila, Ingrid, Carl, John</p>
<p> 7. Camila, Ingrid, Carl, John, Jack</p>
<p> 之后从队列中取出的是Camila。反复执行上述过程,直到队列中的元素只剩一个,这个就是最后的赢家!</p>
<p> 下面是完整的执行结果:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">Camila has been eliminated.
Jack has been eliminated.
Carl has been eliminated.
Ingrid has been eliminated.
The winner is: John</span></pre>
</div>
<p> 下一章我们继续来看看如何用JavaScript来实现链表。</p><br><br>
来源:https://www.cnblogs.com/jaxu/p/11268862.html
頁:
[1]