不折腾的前端,和咸鱼有什么区别html
目录 |
---|
一 目录 |
二 前言 |
三 初阶:模拟实现队列 |
四 初阶:优先队列 |
五 初阶击鼓传花 |
六 进阶:浏览器 Event Loop 机制 |
七 总结 |
返回目录前端
队列,和栈有点相似,可是又不太同样,队列遵循 先进先出 的原则。node
假如将前面学过的栈用堆叠的书来比喻,须要一本一本拿,才能拿到最底部的书来讲的话。git
那么队列就是排队,假如你去银行排队,那么,在前面的人先享受服务,完后前面的人先走。es6
形象点:github
入栈:(底部)A<-B<-C<-D(顶部)
出栈:(底部)A->B->C->D(顶部)
出队列(头部)A<-B<-C<-D 入队列(尾部)
复制代码
返回目录web
在了解了队列后,咱们模拟实现一个队列,加深咱们对队列的印象。面试
首先,为队列声明一些方法:算法
enqueue(element)
:向队列尾部添加一个或者多个新的项。dequeue()
:移除队列的第一(即排在队列最前面的)项,并返回被移除的元素。front()
:返回队列中第一个元素——最早被添加,也将是最早被移除的元素。队列不作任何改动。isEmpty()
:若是队列中不包含任何元素,返回 true
,不然返回 false
。size()
:返回队列中的元素个数,和数组的 length
属性相似。而后,咱们尝试实现这些方法:segmentfault
实现代码:
function Queue() {
const items = [];
// 1. 元素入队列
this.enqueue = function(element) {
items.push(element);
};
// 2. 元素出队列
this.dequeue = function() {
return items.shift();
};
// 3. 查看队列顶部元素
this.front = function() {
return items[0];
};
// 4. 判断队列是否为空
this.isEmpty = function() {
return items.length === 0;
};
// 5. 查看整个队列长度
this.size = function() {
return items.length;
};
// 6. 查看整个队列
this.print = function() {
console.log(items);
}
};
let queue = new Queue();
queue.enqueue('1'); // [ '1' ]
queue.enqueue('2'); // [ '1', '2' ]
queue.dequeue(); // [ '2' ]
queue.dequeue(); // [ ]
queue.print(); // []
复制代码
最后,若是纯粹看 jsliang 写的,没图没视频,小伙伴们很容易懵逼,这里 jsliang 建议看各个大佬的文章或者经过下面章节的几个案例进一步了解:
说到队列,小伙伴们应该对一个词很是有印象:
这时候进行词语联想,就有了 黄牛党,强行插队 等一系列 “恶” 词。
可是,有时候 插队 倒是很是有必要的,例如:
那么,优先队列 如何实现呢?
function PriorityQueue() {
// 1. 定义空数组
const items = [];
// 2. 定义队列
function QueueElement(element, priority) {
this.element = element;
this.priority = priority;
}
// 3. 实现入队列方式
this.enqueue = function(element, priority) {
let queueElement = new QueueElement(element, priority);
let added = false;
for (let i = 0; i < items.length; i++) {
// 若是能够插队,那么就插入到队列,且终止本次循环(减小时间浪费以及重复添加)
if (queueElement.priority < items[i].priority) {
items.splice(i, 0, queueElement);
added = true;
break;
}
}
if (!added) {
items.push(queueElement);
}
};
// 4. 实现队列打印
this.print = function() {
items.forEach((ele) => {
console.log(`${ele.element} ${ele.priority}`);
})
};
// 5. 其余方法和默认的Queue实现相同
}
const priorityQueue = new PriorityQueue();
priorityQueue.enqueue('jsliang', 1);
priorityQueue.enqueue('JavaScriptLiang', 2);
priorityQueue.enqueue('梁峻荣', 1)
priorityQueue.print();
// jsliang 1
// 梁峻荣 1
// JavaScriptLiang 2
复制代码
看完上面代码,能够感觉到 优先队列 给人的感受是高大上的,它能够帮助咱们进行 “更为合理” 地排序,就比如假如你须要写个程序,给急诊用户排队,那么咱们就能够利用 优先队列 的特色,让患者获得最快最准时的治疗。
固然,更多的后续咱们在算法中进行讲解。
击鼓传花是一个游戏。
在这个游戏中,孩子们围成一圈。
给某个孩子一朵花,而后他要尽快把这花传递给下一个孩子(顺序传递)。
某一时刻传花中止,手里拿着花的孩子就被淘汰。
重复这个过程,直到剩下一个孩子。
实现方式以下:
/** * @name 队列模拟 */
function Queue() {
const items = [];
// 1. 元素入队列
this.enqueue = function(element) {
items.push(element);
};
// 2. 元素出队列
this.dequeue = function() {
return items.shift();
};
// 3. 查看队列顶部元素
this.front = function() {
return items[0];
};
// 4. 判断队列是否为空
this.isEmpty = function() {
return items.length === 0;
};
// 5. 查看整个队列长度
this.size = function() {
return items.length;
};
// 6. 查看整个队列
this.print = function() {
console.log(items);
}
};
/** * @name 击鼓传花 * @param {*} nameList 人名 * @param {*} num 淘汰的位置 */
function hotPotato(nameList, num) {
let queue = new Queue();
for (let i = 0; i < nameList.length; i++) {
queue.enqueue(nameList[i]);
}
let eliminated = '';
while (queue.size() > 1) {
for (let i = 0; i < num; i++) {
queue.enqueue(queue.dequeue());
}
eliminated = queue.dequeue();
console.log('淘汰了:' + eliminated);
}
return queue.dequeue();
}
const names = ['name1', 'name2', 'name3', 'name4', 'name5'];
const winner = hotPotato(names, 7);
console.log('赢家是:' + winner);
// 淘汰了:name3
// 淘汰了:name2
// 淘汰了:name5
// 淘汰了:name4
// 赢家是:name1
复制代码
在此次游戏中,淘汰顺序是:
最后剩下 name1
,退出循环,咱们将其推出栈(此时栈为空)。
固然,这里咱们固定了传递进来的数字为 7,若是咱们经过随机来指定一个,那么应该会更加有趣点:
随机式击鼓传花
// ...主体代码如上
const names = ['name1', 'name2', 'name3', 'name4', 'name5'];
const winner = hotPotato(names, Math.floor(Math.random() * 10 + 1));
console.log('赢家是:' + winner);
// 淘汰了:name1
// 淘汰了:name4
// 淘汰了:name2
// 淘汰了:name3
// 赢家是:name5
复制代码
如今淘汰的位置不固定了,是否是以为比起原版的有点味道了~
说到队列,若是单纯讲基础点,相信不少小伙伴都不会买单,因此我们能够进一步探索:
注:这也是面试常备的一道题
首先,为何须要 Event Loop?
由于 JavaScript 是单线程的。
单线程意味着,全部任务都须要排队,前一个任务结束,才会执行后一个任务。
若是前一个任务耗时很长,那么后一个任务就不得不一直等着。
为了协调事件(event),用户交互(user interaction),脚本(script),渲染(rendering),网络(networking)等,用户代理(user agent)必须使用事件循环(event loops)。
而后,了解完 Event Loop 的基础内容,我们经过文章进一步探索 Event Loop。
为此 jsliang 特意翻阅了十几篇文章,从 Event Loop 的机制讲起,经过尝试描述浏览器的 Event Loop,再进一步讲解 Node.js 的 Event Loop,来帮助本身和小伙伴们深刻探索,仔细了解这一块内容:
最后,看到这里,相信小伙伴们对此有个简单了解,对队列这个词也有了进一步的深刻了解。
参考文献:
这样,咱们就 暂时 完成了队列的基础了解学习,由于你站在甲板上,你是看不到整艘邮轮是怎么运行的,因此我们会慢慢探索,经过各类 LeetCode 题专门训练,逐步丰富咱们的视野。
在探索这些内容的过程当中,jsliang 也对某些内容感到困惑,基本上,碰到一些新内容,会先抄一遍(照着敲一份),细细理解它的意思,而后根据自身理解,想一想有没有其余实现方式(例如经过定时器来实现击鼓传花)。
我的以为,人的一辈子都在学习,关键是你能不能持续探索下去,以及你的探索方式对你的启发。
仅此而已。
不折腾的前端,和咸鱼有什么区别!
jsliang 会天天更新一道 LeetCode 题解,从而帮助小伙伴们夯实原生 JS 基础,了解与学习算法与数据结构。
浪子神剑 会天天更新面试题,以面试题为驱动来带动你们学习,坚持天天学习与思考,天天进步一点!
扫描上方二维码,关注 jsliang 的公众号(左)和 浪子神剑 的公众号(右),让咱们一块儿折腾!
jsliang 的文档库 由 梁峻荣 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。
基于github.com/LiangJunron…上的做品创做。
本许可协议受权以外的使用权限能够从 creativecommons.org/licenses/by… 处得到。