重读《学习JavaScript数据结构与算法-第三版》- 第4章 栈
定场诗
金山竹影几千秋,云索高飞水自流;
万里长江飘玉带,一轮银月滚金球。
远自湖北三千里,近到江南十六州;
美景一时观不透,天缘有分画中游。
前言
本章是重读《学习JavaScript数据结构与算法-第三版》的系列文章,本章为各位小伙伴分享数据结构-栈的故事,请让胡哥带你走进栈的世界
栈
何为栈?栈是一种遵从后进先出(LIFO)原则的有序集合。
新添加或待删除的元素都保存在栈的同一端,称作栈顶;另一端就叫栈底。
在栈里,新元素都靠近栈顶,旧元素都接近栈底。
基于数组的栈
我们将创建一个基于数组的栈,了解栈的结构、运行规则
/**
* 基于数组array的栈Stack
* @author huxiaoshuai
*/
class Stack {
// 初始化
constructor () {
this.items = []
}
}
使用数组保存栈里的元素
数组允许我们在任何位置添加和删除元素,那基于栈遵循LIFO原则,我们对元素的插入和删除功能进行封装
| 方法 | 描述 |
|---|---|
| push(element(s)) | 添加一个(或多个)新元素到栈顶 |
| pop() | 移除栈顶元素,同时返回被移除的元素 |
| peek() | 返回栈顶的元素,不对栈做任何修改 |
| isEmpty() | 判断栈是否为空,为空则返回true,否则返回false |
| clear() | 移除栈的所有元素 |
| size() | 返回栈的元素个数 |
代码实现
class Stack {
// 初始化
constructor () {
this.items = []
}
/**
* push() 添加元素到栈顶
*/
push (element) {
this.items.push(element)
}
/**
* pop() 移除栈顶元素并返回
*/
pop () {
return this.items.pop()
}
/**
* peek() 返回栈顶部元素
*/
peek () {
return this.items[this.items.length - 1]
}
/***
* isEmpty() 检测栈是否为空
*/
isEmpty () {
return this.items.length === 0
}
/**
* size() 返回栈的长度
*/
size () {
return this.items.length
}
/**
* clear() 清空栈元素
*/
clear () {
this.items = []
}
}
使用Stack类
const stack = new Stack()
console.log(stack.isEmpty()) // true
// 添加元素
stack.push(5)
stack.push(8)
// 输出元素
console.log(stack.peek()) // 8
stack.push(11)
console.log(stack.size()) // 3
console.log(stack.isEmpty()) // false
stack.push(15)
基于以上栈操作的示意图

stack.pop()
stack.pop()
console.log(stack.size()) // 2
基于以上栈操作的示意图

基于对象的栈
创建一个Stack类最简单的方式是使用一个数组来存储元素。在处理大量数据的时候,我们同样需要评估如何操作数据是最高效的。
使用数组时,大部分方法的时间复杂度是O(n)。简单理解:O(n)的意思为我们需要迭代整个数组直到找到要找的那个元素,在最坏的情况下需要迭代数组的所有位置,其中的n代表数组的长度。数组越长,所需时间会更长。另外,数组是元素的一个有序集合,为保证元素的有序排列,会占用更多的内存空间。
使用JavaScript对象来存储所有的栈元素,以实现可以直接获取元素,同时占用较少的内存空间,同时保证所有的元素按照我们的需要进行排列,遵循后进先出(LIFO)原则。
代码实现
/**
* 基于对象的Stack类
* @author huxiaoshai
*/
class Stack {
// 初始化
constructor () {
this.items = {}
this.count = 0
}
/**
* push() 向栈中添加元素
*/
push (element) {
this.items[this.count] = element
this.count++
}
/**
* isEmpty() 判断是否为空
*/
isEmpty () {
return this.count === 0
}
/**
* size() 返回栈的长度
*/
size () {
return this.count
}
/**
* pop() 栈顶移除元素并返回
*/
pop () {
if (this.isEmpty()) {
return undefined
}
this.count--
let result = this.items[this.count]
delete this.items[this.count]
return result
}
/**
* peek() 返回栈顶元素,如果为空则返回undefined
*/
peek () {
if (this.isEmpty()) {
return undefined
}
return this.items[this.count - 1]
}
/**
* clear() 清空栈数据
*/
clear () {
this.items = {}
this.count = 0
}
/**
* toString() 实现类似于数组结构打印栈内容
*/
toString () {
if (this.isEmpty()) {
return ''
}
let objStr = `${this.items[0]}`
for (let i = 1; i < this.count; i++) {
objStr = `${objStr},${this.items[i]}`
}
return objStr
}
}
保护数据结构内部元素
私有属性
有时候我们需要创建供其他开发者使用的数据结构和对象时,我们希望保存内部元素,只有使用允许的方法才能修改内部结构。很不幸,目前JS是没有办法直接声明私有属性的,目前业内主要使用一下几种方式实现私有属性。
下划线命名约定
class Stack {
constructor () {
this._items = {}
this._count = 0
}
}
这只是约定,一种规范,并不能实际保护数据
基于ES6的限定作用域Symbol实现类
const _items = Symbol('stackItems')
class Stack {
constructor () {
this[_items] = []
}
}
假的私有属性,ES6新增的Object.getOwnPropertySymbols方法能够获取类里面声明的所有Symbols属性
基于ES6的WeakMap实现类
/**
* 使用WeekMap实现类的私有属性
*/
const items = new WeakMap()
console.log(items) // WeakMap { [items unknown] } class Stack {
constructor () {
items.set(this, [])
} push (element) {
const s = items.get(this)
s.push(element)
} pop () {
const s = items.get(this)
const r = s.pop()
return r
} toString () {
const s = items.get(this)
return s.toString()
}
} const stack = new Stack()
stack.push(1)
stack.push(2)
stack.push(3) console.log(stack.toString()) // 1,2,3
console.log(stack.items) // undefined
使用该方式,items是Stack类里的私有属性,但是此种方式代码的可读性不强,而且在扩展该类时无法继承私有属性。
ECMAScript类属性提案
有一个关于JavaScript类中增加私有属性的提案。通过在属性前添加井号(#)作为前缀来声明私有属性。
class Stack {
#count = 0
#items = []
}
使用栈来解决问题
栈的实际应用非常广泛。在回溯问题中,它可以存储访问过的任务或路径、撤销的操作(后续会在讨论图和回溯问题时进一步详细讲解)。栈的使用场景有很多,如汉诺塔问题、平衡圆括号、计算机科学问题:十进制转二进制问题
/**
* decimalToBinary() 实现十进制转二进制的算法
*/
function decimalToBinary (decNumber) {
// 实例化栈数据结构
const remStack = new Stack()
let number = decNumber
let rem;
let binaryString = ''
// 依次将获取的二进制数压入栈中
while (number > 0) {
rem = Math.floor(number % 2)
remStack.push(rem)
number = Math.floor(number / 2)
}
// 拼接要输出的二进制字符串
while (!remStack.isEmpty()) {
binaryString += remStack.pop().toString()
}
return binaryString
}
console.log(decimalToBinary(10)) // 1010
console.log(decimalToBinary(23)) // 10111
后记
以上就是胡哥今天给大家分享的内容,喜欢的小伙伴记得收藏、转发、点击右下角按钮在看,推荐给更多小伙伴呦,欢迎多多留言交流...
胡哥有话说,一个有技术,有情怀的胡哥!京东开放平台首席前端攻城狮。与你一起聊聊大前端,分享前端系统架构,框架实现原理,最新最高效的技术实践!
长按扫码关注,更帅更漂亮呦!关注胡哥有话说公众号,可与胡哥继续深入交流呦!

重读《学习JavaScript数据结构与算法-第三版》- 第4章 栈的更多相关文章
- 重读《学习JavaScript数据结构与算法-第三版》- 第5章 队列
定场诗 马瘦毛长蹄子肥,儿子偷爹不算贼,瞎大爷娶个瞎大奶奶,老两口过了多半辈,谁也没看见谁! 前言 本章为重读<学习JavaScript数据结构与算法-第三版>的系列文章,主要讲述队列数据 ...
- 重读《学习JavaScript数据结构与算法-第三版》-第2章 ECMAScript与TypeScript概述
定场诗 八月中秋白露,路上行人凄凉: 小桥流水桂花香,日夜千思万想. 心中不得宁静,清早览罢文章, 十年寒苦在书房,方显才高志广. 前言 洛伊安妮·格罗纳女士所著的<学习JavaScript数据 ...
- 重读《学习JavaScript数据结构与算法-第三版》- 第6章 链表(一)
定场诗 伤情最是晚凉天,憔悴厮人不堪言: 邀酒摧肠三杯醉.寻香惊梦五更寒. 钗头凤斜卿有泪,荼蘼花了我无缘: 小楼寂寞新雨月.也难如钩也难圆. 前言 本章为重读<学习JavaScript数据结构 ...
- 重读《学习JavaScript数据结构与算法-第三版》- 第3章 数组(一)
定场诗 大将生来胆气豪,腰横秋水雁翎刀. 风吹鼍鼓山河动,电闪旌旗日月高. 天上麒麟原有种,穴中蝼蚁岂能逃. 太平待诏归来日,朕与先生解战袍. 此处应该有掌声... 前言 读<学习JavaScr ...
- 学习JavaScript数据结构与算法 (二)
学习JavaScript数据结构与算法 的笔记 包含第四章队列, 第五章链表 本人所有文章首发在博客园: http://www.cnblogs.com/zhangrunhao/ 04队列 实现基本队列 ...
- 学习JavaScript数据结构与算法 (一)
学习JavaScript数据结构与算法 的笔记, 包含一二三章 01基础 循环 斐波那契数列 var fibonaci = [1,1] for (var i = 2; i< 20;i++) { ...
- 学习JavaScript数据结构与算法---前端进阶系列
学习建议 1.视频学习---认知 建议:在中国慕课上找"数据结构"相关的视频教程.中国大学MOOC 推荐清华大学.北京大学.浙江大学的教程,可先试看,然后根据自身的情况选择视频进行 ...
- 学习Javascript数据结构与算法(第2版)笔记(1)
第 1 章 JavaScript简介 使用 Node.js 搭建 Web 服务器 npm install http-server -g http-server JavaScript 的类型有数字.字符 ...
- 学习JavaScript数据结构与算法 2/15
第一章 JavaScript简介 js不同于C/C++,C#,JAVA,不是强类型语言. 通常,代码质量可以用全局变量和函数的数量来考量(数量越多越糟).因此,尽可能避免使用全局变量. JS数据类型 ...
随机推荐
- 9.16考试 第一题 X国的军队题解
这道题总体来看还是比较满意的.连想带打不超过半个小时,打了不到当时基本读懂后就感觉是贪心,但贪什么很重要,当时一开始想的是贪心死亡人数,从小到大搞,然后自己造了几个小数据,还好WA了,然后又列了一个式 ...
- STM32F0_HAL库驱动描述——LL驱动程序概述
LL驱动概述 低层(LL)驱动器旨在提供快速轻量级的专家导向层,它比硬件更接近硬件: 与HAL相反,LLAPI不适用于优化访问不是关键功能的外设设备,或者需要大量软件配置和/或复杂的高级堆栈(如USB ...
- PipelineDB Install and Test
Installation Prerequisites: CentOS Linux release 7.2.1511 (Core) Download [root@citus1 ~]# wget http ...
- python如何将一个多位数数值转换为列表类型
现在:a = 10,由于暂时没找到更好的方法,且使用下面的方法进行转换. 目标:转化为['10'] 以下为错误尝试: 1.直接转换,提示整型对象不可迭代. 2.先转换为字符串,再转换为列表,发现被分成 ...
- 用了三星Dex,我已经快一个月回家没开过电脑了
其实比较早就知道手机使用显示屏扩展的功能,但是以前的技术可能受性能影响体验还不太好.后来让我期待的是Linux On Dex这个项目知道了手机已经如此强大了,可惜只能是是特定机器,因此在618之际乘着 ...
- python函数知识一 函数初始、定义与调用、返回值、参数和函数的好处+菜中菜
第四章 函数 1.函数初识: def :关键字 -- 定义 函数名:和变量的定义方式一样 (): 用于参数传递,: 形参:函数的定义中()内的是形参 实参:调用的()内是实参 传参:调用时将实参传递给 ...
- MsgWaitForMultipleObjects
Use caution when calling the wait functions and code that directly or indirectly creates windows. If ...
- 【转】Spring事务详解
1.事务的基本原理 Spring事务的本质其实就是数据库对事务的支持,使用JDBC的事务管理机制,就是利用java.sql.Connection对象完成对事务的提交,那在没有Spring帮我们管理事务 ...
- C#3.0新增功能02 匿名类型
连载目录 [已更新最新开发文章,点击查看详细] 匿名类型提供了一种方便的方法,可用来将一组只读属性封装到单个对象中,而无需首先显式定义一个类型. 类型名由编译器生成,并且不能在源代码级使用. 每 ...
- 关于Object.defineProperty 的基础知识
Object.defineProperty 这个方法大家耳熟能详,可以对 对象的属性进行添加或修改的操作.即可以进行 数据劫持 .vue就是通过这个方法来劫持数据的. 平时我们创建对象的时候,一般通 ...