2020年6月4日下午,前端小伙伴胡利朋同学分享了数据结构中栈和队列的 JavaScript 实现,收获良多,在此做下笔记,也自己实现一下栈和队列这两个数据结构。
1.栈
1.1 栈的定义
栈是一种特殊的线性表,仅能够在栈顶进行操作,有着后进先出的特性 。

1.2 栈的 JavaSaript 实现
1.2.1数据存储
- 数组
- 链表
栈这样的数据结构,一般可以用数组或者链表进行数据的存储。在JavaScript中,数组最常用,也是最熟悉的,下面我们就用数组来实现栈这个数据结构。
1.2.2栈的方法
栈的方法主要有以下几个:
- 入栈
- 出栈
- 返回栈顶元素
- 判空
- 清空
- 返回大小
- 打印所有元素
具体实现:
class Stack {constructor() {// 定义一个数组存储数据,数组头为栈底,尾为栈顶this.stack = [];}// 入栈:添加一个或多个元素到栈顶inStack(...args) {this.stack.push(...args);}// 出栈:移除栈顶的元素,同时返回被移除的元素outStack() {return this.stack.pop();}// 返回栈顶元素stackTop() {return this.stack[this.stack.length - 1];}// 判空isEmpty() {return this.stack.length === 0;}// 清空clearStack() {this.stack = [];}// 返回大小stackSize() {return this.stack.length;}// 打印所有元素printStack() {console.log(this.stack.toString());}}// 测试let new_stack = new Stack();// 入栈new_stack.inStack(1, 2, 3, 4)console.log('打印入栈之后栈中所有元素')new_stack.printStack();console.log('返回栈顶元素', new_stack.stackTop());console.log('返回栈的大小', new_stack.stackSize());console.log('判断栈是否为空', new_stack.isEmpty());console.log('出栈', new_stack.outStack());console.log('打印出栈之后栈中所有元素')new_stack.printStack();
1.3 栈的应用练习
创建好栈这个数据结构之后,很多算法的时间都会变得很简单,下面就以判断括号合法性这一算法来举例说明。
判断括号的合法性
举例:
- ‘(()()((((()))))(()))’ —合法的括号,左右配对
- ‘())())(()()(()))()’ —不合法的括号,左右不配对
算法实现:
// 栈的应用练习// 判断括号的合法性// 整体思路:// 如果只有一个括号,直接不合法// 如果第一个是")",直接不合法// 创建一个栈,遍历字符串,如果是"(",将其入栈,当碰到")"的时候,将一个"("出栈,当没有"("可以出栈和")"匹配,直接不合法,最后栈不为空(还有"("没有被匹配),直接不合法function isLegalBracket(str) {// 输入的字符串为空,返回提示信息if (str.length === 0) {return '请输入要检验的字符串';}// 只有一个括号,返回falseif (str.length === 1) {return false;}// 第一个是")",返回falseif (str[0] === ")") {return false;}let stack = new Stack;for (let i = 0; i < str.length; i++) {if (str[i] === "(") {stack.inStack(str[i]);}if (str[i] === ")") {if (stack.isEmpty()) {return false;} else {stack.outStack();}}}return stack.isEmpty();}console.log("'(()()((((()))))(()))'中括号的合法性为:",isLegalBracket('(()()((((()))))(()))'))console.log("'())())(()()(()))()'中括号的合法性为:",isLegalBracket('())())(()()(()))()'))
栈除了上面的应用,还可以实现一些操作的撤回以及重做,都是很方便的,碰到需要做撤回的功能,小伙伴们可以考虑用栈来存储数据哦!
2. 队列
2.1 队列的定义
队列是一种特殊的线性表,其特殊之处在于,它只允许你在队列的头部删除元素,在队列的末尾添加新的元素。有着先进先出的特性。


2.2 队列的 JavaScript实现
2.2.1 数据存储
- 数组
- 链表
同栈一样,我们经常用数组或者链表来存储队列这一数据结构,下面来看看用数组怎么存储队列吧!
2.2.2 队列的方法
和栈类似,队列中一般也都有以下方法
- 队尾添加
- 队首删除
- 返回队首元素
- 判断队列是否为空
- 清空队列
- 返回队列的大小
- 打印队列中所有的元素
具体实现
class Queue {constructor() {// 定义一个数组存储数据,数组头为队首,尾为队尾this.queue = [];}// 入队:添加一个或多个元素到队列中inQueue(...args) {this.queue.push(...args);}// 出队:移除队首的元素,同时返回被移除的元素outQueue() {return this.queue.shift();}// 返回队首元素queueTop() {return this.queue[0];}// 判空isEmpty() {return this.queue.length === 0;}// 清空clearQueue() {this.queue = [];}// 返回大小queueSize() {return this.queue.length;}// 打印所有元素printQueue() {console.log(this.queue.toString());}}
2.3 队列的应用练习
使用队列这一数据结构,也可以让我们很多方法变得很简单,比如我们要查询斐波那契数列中第200个数是什么,就可以用队列很方便的算出来。
斐波那契数列:一个数列,从第三项开始,以后的每一项为前两项的和
例:1,1,2,3,5,8,13,21,34…
// 斐波那契数列function fibonacci(n) {let queue = new Queue();let index = 0;// 先放⼊入斐波那契序列列的前两个数值queue.inQueue(1);queue.inQueue(1);while (index < n - 2) {// 出队列列⼀一个元素let del_item = queue.outQueue();// 取队列列头部元素let head_item = queue.queueTop();let next_item = del_item + head_item;// 将计算结果放⼊入队列列queue.inQueue(next_item);index += 1;}queue.outQueue();return queue.queueTop();};console.log(fibonacci(4))console.log(fibonacci(5))console.log(fibonacci(6))console.log(fibonacci(7))console.log(fibonacci(8))console.log(fibonacci(99))
除此以外,在写前端交互的时候,页面上会有很多弹窗,我们也可以用队列来存储,这样,弹窗就会按照出现的顺序一个一个弹出来。在后端开发中,服务器的消息队列,也经常使用队列这种数据结构。
2.4 队列的变形—双端队列
2.4.1 定义
双端队列(deque,或称 double-ended queue)是一种允许我们同时从前端和后端添加和移除元素的特殊队列。(看这个定义,双端队列是不是特别像JavaSctipt中的数组?)
2.4.2 实现

2.4.3 应用
回文检查器
回文字符串:当一个字符串是一个中心对称的字符串,我们就称这个字符串为回文字符串
例:”a”
例:”aba”
例:”abba”
// 回文检查器function palChecker(str) {// 创建一个双端队列,将字符串中的字符,依次添加到双端队列中let arr = str.split('');// 假定是回文字符串let isPalindrome = true;// 当至少有一个字符并且满足首位相等,则循环while (arr.length > 1 && isPalindrome) {let first = arr.shift();let last = arr.pop();if (first !== last) {isPalindrome = false;}}return isPalindrome;}console.log(palChecker('abba'))
队列还有其他的变形,如
,小伙伴们可以自行了解,这里就不再展开解释。
