泳镜 發表於 2019-7-30 15:17:00

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 &lt;<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>&nbsp;  我们用一个小游戏“击鼓传花”来说明循环队列在实际中的应用。</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() &gt; 1<span style="color: rgba(0, 0, 0, 1)">) {
      </span><span style="color: rgba(0, 0, 255, 1)">for</span> (let i = 0; i &lt; 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>&nbsp;  下一章我们继续来看看如何用JavaScript来实现链表。</p><br><br>
来源:https://www.cnblogs.com/jaxu/p/11268862.html
頁: [1]
查看完整版本: JavaScript数据结构——队列的实现与应用