金山竹影几千秋,云索高飞水自流;
万里长江飘玉带,一轮银月滚金球。
远自湖北三千里,近到江南十六州;
美景一时观不透,天缘有分画中游。复制代码
本章是重读《学习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 = []
}
}复制代码
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)复制代码
基于以上栈操做的示意图this
stack.pop()
stack.pop()
console.log(stack.size()) // 2复制代码
基于以上栈操做的示意图spa
建立一个Stack类最简单的方式是使用一个数组来存储元素。在处理大量数据的时候,咱们一样须要评估如何操做数据是最高效的。3d
使用数组时,大部分方法的时间复杂度是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复制代码
以上就是胡哥今天给你们分享的内容,喜欢的小伙伴记得收藏
、转发
、点击右下角按钮在看
,推荐给更多小伙伴呦,欢迎多多留言交流...
胡哥有话说,一个有技术,有情怀的胡哥!京东开放平台首席前端攻城狮。与你一块儿聊聊大前端,分享前端系统架构,框架实现原理,最新最高效的技术实践!
长按扫码关注,更帅更漂亮呦!关注胡哥有话说公众号,可与胡哥继续深刻交流呦!