队列与栈不同,它遵从先进先出(FIFO——First In First Out)原则,新添加的元素排在队列的尾部,元素只能从队列头部移除。
我们在前一篇文章中描述了如何用java script来实现栈这种数据结构,这里我们对应地来实现队列。
function Queue() { let items = []; // 向队列添加元素(一个或多个) this.enqueue = function (element) { if (element instanceof Array) items = items.concat(element); else items.push(element); }; // 从队列移除元素 this.dequeue = function () { return items.shift(); }; // 返回队列中的第一个元素 this.front = function () { return items[0]; }; // 判断队列是否为空 this.isEmpty = function () { return items.length === 0; }; // 返回队列的长度 this.size = function () { return items.length; }; // 清空队列 this.clear = function () { items = []; }; // 打印队列内的所有元素 this.print = function () { console.log(items.toString()); }; }
与栈的实现方式类似,唯一不同的是从队列移除元素时取的是队列头部的元素(最先添加的),而栈则是取的顶部元素(最后添加的)。下面是一些测试用例及返回结果:
let queue = new Queue(); console.log(queue.isEmpty()); // true queue.enqueue('John'); queue.enqueue(['Jack', 'Camila']); queue.print(); // John,Jack,Camila console.log(queue.size()); // 3 console.log(queue.isEmpty()); // false console.log(queue.front()); // John console.log(queue.dequeue()); // John queue.print(); // Jack,Camila queue.clear(); queue.print(); //
注意,我们允许批量向队列中添加元素,为此我们需要判断enqueue方法的参数类型,如果参数是数组,则用concat()函数连接两个数组,如果参数不是数组,则直接用push()函数将元素添加到队列中。
与栈的实现方式一样,这里我们也同样给出用ES6的WeakMap类来实现的队列版本。
let Queue = (function () { const items = new WeakMap(); class Queue { constructor() { items.set(this, []); } enqueue (element) { let q = items.get(this); if (element instanceof Array) items.set(this, q.concat(element)); else q.push(element); }; dequeue () { let q = items.get(this); return q.shift(); }; front () { return items.get(this)[0]; }; isEmpty () { return items.get(this).length === 0; }; size () { return items.get(this).length; }; clear () { items.set(this, []); }; print () { console.log(items.get(this).toString()); }; } return Queue; })();
这两个版本的执行结果是一样的,它们的区别我们在前一篇文章中已经提及过了,这里不再赘述。
优先队列
所谓优先队列,顾名思义,就是说插入到队列中的元素可以根据优先级设置先后顺序。优先级越高位置越靠前,优先级越低位置越靠后。假设优先级用数字来表示,如果数字越小表示的优先级越高,形成的队列就称之为最小优先队列,反之则称之为最大优先队列。下面是实现的代码:
function PriorityQueue() { let items = []; // 向队列添加元素(一个或多个) // 参数obj的数据格式:{element, priority} this.enqueue = function (obj) { if (obj instanceof Array) { for (let i = 0, ci; ci = obj[i]; i++) { this.enqueue(ci); } } else { let added = false; for (let i = 0, ci; ci = items[i]; i++) { // 最小优先级,即将priority值小的元素插入到队列的前面 if (obj.priority < ci.priority) { items.splice(i, 0, obj); added = true; break; } } // 如果元素没有插入到队列中,则默认加到队列的尾部 if (!added) items.push(obj); } }; // 从队列移除元素 this.dequeue = function () { return items.shift(); }; // 返回队列中的第一个元素 this.front = function () { return items[0]; }; // 判断队列是否为空 this.isEmpty = function () { return items.length === 0; }; // 返回队列的长度 this.size = function () { return items.length; }; // 清空队列 this.clear = function () { items = []; }; // 打印队列内的所有元素 this.print = function () { items.forEach(function (item) { console.log(`${item.element} - ${item.priority}`); }); }; }
可以看到,唯一有区别的只有enqueue方法。我们规定所有添加到优先队列的元素都必须满足{element, priority}这种JSON格式,以保证队列中的每一个元素都有一个priority属性来表示优先级。如果要添加的元素的优先级和队列中已有元素的优先级