定场诗

金山竹影几千秋,云索高飞水自流;
万里长江飘玉带,一轮银月滚金球。
远自湖北三千里,近到江南十六州;
美景一时观不透,天缘有分画中游。

前言

本章是重读《学习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是没有办法直接声明私有属性的,目前业内主要使用一下几种方式实现私有属性。

  1. 下划线命名约定

    class Stack {
    constructor () {
    this._items = {}
    this._count = 0
    }
    }

    这只是约定,一种规范,并不能实际保护数据

  2. 基于ES6的限定作用域Symbol实现类

    const _items = Symbol('stackItems')
    class Stack {
    constructor () {
    this[_items] = []
    }
    }

    假的私有属性,ES6新增的Object.getOwnPropertySymbols方法能够获取类里面声明的所有Symbols属性

  3. 基于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类里的私有属性,但是此种方式代码的可读性不强,而且在扩展该类时无法继承私有属性。

  4. 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章 栈的更多相关文章

  1. 重读《学习JavaScript数据结构与算法-第三版》- 第5章 队列

    定场诗 马瘦毛长蹄子肥,儿子偷爹不算贼,瞎大爷娶个瞎大奶奶,老两口过了多半辈,谁也没看见谁! 前言 本章为重读<学习JavaScript数据结构与算法-第三版>的系列文章,主要讲述队列数据 ...

  2. 重读《学习JavaScript数据结构与算法-第三版》-第2章 ECMAScript与TypeScript概述

    定场诗 八月中秋白露,路上行人凄凉: 小桥流水桂花香,日夜千思万想. 心中不得宁静,清早览罢文章, 十年寒苦在书房,方显才高志广. 前言 洛伊安妮·格罗纳女士所著的<学习JavaScript数据 ...

  3. 重读《学习JavaScript数据结构与算法-第三版》- 第6章 链表(一)

    定场诗 伤情最是晚凉天,憔悴厮人不堪言: 邀酒摧肠三杯醉.寻香惊梦五更寒. 钗头凤斜卿有泪,荼蘼花了我无缘: 小楼寂寞新雨月.也难如钩也难圆. 前言 本章为重读<学习JavaScript数据结构 ...

  4. 重读《学习JavaScript数据结构与算法-第三版》- 第3章 数组(一)

    定场诗 大将生来胆气豪,腰横秋水雁翎刀. 风吹鼍鼓山河动,电闪旌旗日月高. 天上麒麟原有种,穴中蝼蚁岂能逃. 太平待诏归来日,朕与先生解战袍. 此处应该有掌声... 前言 读<学习JavaScr ...

  5. 学习JavaScript数据结构与算法 (二)

    学习JavaScript数据结构与算法 的笔记 包含第四章队列, 第五章链表 本人所有文章首发在博客园: http://www.cnblogs.com/zhangrunhao/ 04队列 实现基本队列 ...

  6. 学习JavaScript数据结构与算法 (一)

    学习JavaScript数据结构与算法 的笔记, 包含一二三章 01基础 循环 斐波那契数列 var fibonaci = [1,1] for (var i = 2; i< 20;i++) { ...

  7. 学习JavaScript数据结构与算法---前端进阶系列

    学习建议 1.视频学习---认知 建议:在中国慕课上找"数据结构"相关的视频教程.中国大学MOOC 推荐清华大学.北京大学.浙江大学的教程,可先试看,然后根据自身的情况选择视频进行 ...

  8. 学习Javascript数据结构与算法(第2版)笔记(1)

    第 1 章 JavaScript简介 使用 Node.js 搭建 Web 服务器 npm install http-server -g http-server JavaScript 的类型有数字.字符 ...

  9. 学习JavaScript数据结构与算法 2/15

    第一章 JavaScript简介 js不同于C/C++,C#,JAVA,不是强类型语言. 通常,代码质量可以用全局变量和函数的数量来考量(数量越多越糟).因此,尽可能避免使用全局变量. JS数据类型 ...

随机推荐

  1. ServiceFabric极简文档-5.0 Service Fabric有状态与无状态

    Service Fabric 应用程序方案 2017/08/14 作者 Edward Chen Jack Zeng Azure Service Fabric提供了一个可靠而灵活的平台,可用于编写和运行 ...

  2. MyBatis if标签的用法

    <!-- 4.1.1 在WHERE条件中使用if 需求: 实现一个用户管理高级查询功能,根据输入的条件去检索用户信息.这个功能 还需要支持以下三种情况:当只有输入用户名时,需要根据用户名进行模糊 ...

  3. Prim算法与Kruskal(没有代码)

    两个最小生成树算法, 都有一个共同的思想: 这棵树是一点一点长大的; 并且每次生长, 都是贪心的. 不同之处是: Kruscal算法是以边为中心的, 每次找最小的并且有用的边添加到树上; Prim算法 ...

  4. Maven(一)Maven 的概念和安装

    Maven 的概念和安装 Maven 是什么 首先 Maven 肯定是一个造福人类的好东西,它可以省去我们构建项目中引入 jar 包时的麻烦,还有利于项目的模块化开发等等等好处.在如今项目中大体都是使 ...

  5. Spring 注解编程之 AnnotationMetadata

    在上篇文章 Spring 注解编程之模式注解 中我们讲到 Spring 模式注解底层原理,依靠 AnnotationMetadata 接口判断是否存在指定元注解. 这篇文章我们主要深入 Annotat ...

  6. linux初学者-文件的归档和传输

      1.文件归档 因为linux系统都是以文件的形式存在,所以在处理文件时有时候因为文件太多导致传输速度慢等问题,为了提高方便并且提高效率,常把文件归档,文件归档就是把多个文件变成一个归档文件. 文件 ...

  7. java练习---8

    //程序员:罗元昊 2017.10.16 题目3.7 import java.util.Scanner; public class L { @SuppressWarnings("resour ...

  8. Ubuntu16.04配置Django服务器环境

    本环境是使用Ubuntu16.04+Django2.1.0+python3.6+uwsgi+nginx进行配置 1. 安装python3.6 Ubuntu16.04中原本安装的是python3.5,但 ...

  9. Codeforces Round #486 (Div. 3) C "Equal Sums" (map+pair<>)

    传送门 •题意 给k个数列,从中k个数列中找出任意2个数列 i ,j 使得数列i删除第x个数,和数列j删除第y个数的和相等 若存在,输出 i ,x 和 j,y •思路 每个数列之间的联系为数列的和之间 ...

  10. 滚动视图、列表视图[ListView、SimpleAdapter类]

    滚动视图 <ScrollView android: layout_width="fill_parent" android: layout_height="fill_ ...