每周一练 之 数据结构与算法(Stack)


Posted in Javascript onApril 16, 2019

最近公司内部在开始做前端技术的技术分享,每周一个主题的 每周一练,以基础知识为主,感觉挺棒的,跟着团队的大佬们学习和复习一些知识,新人也可以多学习一些知识,也把团队内部学习氛围营造起来。

我接下来会开始把每周一练的题目和知识整理一下,便于思考和巩固,就像今天这篇开始。

学习的道路,很漫长,要坚持,希望大家都能掌握自己喜欢的技术,和自己需要的技术。

本周练习内容:数据结构与算法 —— Stack

这些都是数据结构与算法,一部分方法是团队其他成员实现的,一部分我自己做的,有什么其他实现方法或错误,欢迎各位大佬指点,感谢。

一、栈有什么特点,生活中有什么例子?

  1. 栈( stack )又称堆栈,是一种后进先出的有序集合,其中一端为栈顶,另一端为栈底,添加元素(称为压栈/入栈或进栈)时,将新元素压入栈顶,删除元素(称为出栈或退栈)时,将栈底元素删除并返回被删除元素。
  2. 特点:先进后出,后进先出。
  3. 例子:一叠书、一叠盘子。

 每周一练 之 数据结构与算法(Stack)

二、实现一个栈,并实现下面方法

  1. push(element):添加一个新元素到栈顶。
  2. pop():移除栈顶的元素,同时返回被移除的元素。
  3. peek():返回栈顶的元素,不对栈做任何修改 (这个方法不会移除栈顶的元素,仅仅返回它)。
  4. isEmpty():如果栈没有任何元素就返回 true,否则返回 false。
  5. clear():移除栈里面的所有元素。
  6. size():返回栈里的元素个数。这个方法与数组的 length 属性类似。

方法1:ES6实现

class Stack {
  constructor (){
    this.items = []
  }
  push( element ){
    this.items.push(element)
  }
  pop(){
    return this.items.pop()
  }
  peek(){
    return this.items[this.items.length - 1]
  }
  isEmpty(){
    return this.items.length === 0
  }
  clear(){
    this.items = []
  }
  size(){
    return this.items.length
  }
}

上面实现的方式虽然简单,但是内部 items 属性是公共的,为了满足面向对象变成私有性的原则,我们应该让 items 作为私有属性,因此我们可以使用 ES6 中 Symbol 或 WeakMap 来实现:

方法2:使用 ES6 的 Symbol 基本数据类型实现
知识点复习:ES6 中的 Symbol 介绍

const _items = Symbol()
class Stack {
  constructor (){
    this[_items] = []
  }
  push (element){
    this[_items].push(element)
  }
  // 剩下方法和第一种实现的差不多,这里省略
  // 只要把前面方法中的 this.items 更改为 this[_items]
}

 方法3:使用 ES6 的 WeakMap 实现

知识点复习:ES6 中的 WeakMap 介绍

 

const items = new WeakMap()
class Stack {
  constructor (){
    items.set(this, [])
  }
  push (element){
    let item = items.get(this)
    item.push(element)
  }
  // 剩下方法和第一种实现的差不多,这里省略
  // 只要把前面方法中的获取 this.items 的方式,更改为 items.get(this) 获取
}

 三、编写一个函数,实现十进制转二进制

题目意思很简单,就是十进制转二进制,但是在实际工作开发中,我们更愿意实现的是任意进制转任意进制,不过呢,我们还是以解决问题为首要目标呀。

当然,业务需求可以直接使用 toString(2) 方法,但是为了练习,咱还是不这么用咯。

方法1:使用前面定义的 Stack 类

这里使用前面题目中定义的 Stack 类。

/**
 * 十进制转换为二进制
 * @param {Number} bit 
 */
function bitset (bit){
  if(bit == 0) return '0'
  if(!/^[0-9]+.?[0-9]*$/.test(bit)){
    return new Error('请输入正确的数值!')
  }

  let stack = new Stack(), result = ''
  while (bit > 0){
    stack.push(bit % 2)
    bit = Math.floor(bit / 2)
  }
  while (!stack.isEmpty()){
    result += stack.pop().toString()
  }
  return result

}

方法2:简单实现

下面这个方法,其实不太好,因为没有怎么用到这次要练习的栈方法,哈哈。

/**
 * 十进制转换为二进制
 * @param {Number} bit 
 */
function bitset (bit){
  if(bit == 0) return '0'
  if(!/^[0-9]+.?[0-9]*$/.test(bit)){
    return new Error('请输入正确的数值!')
  }

  let arr = []
  while(bit > 0){
    arr.push(bit % 2)
    bit = Math.floor(bit / 2)
  }
  return arr.reverse().join('')
}

另外可以参考:wikiHow - 从十进制转换为二进制。

四、编写一个函数,实现检验圆括号顺序的有效性

主要目的就是:该函数接收一个圆括号字符串,判断里面的括号顺序是否有效,如果有效则返回 true 反之 false。
如:

  1. (   -> false
  2. ()  -> true
  3. (() -> false
  4. ()) -> false
  5. ()) -> false
  6. (((()()))()) -> true

这个题目实现的主要方法是:遍历字符串,先排除错误情况,然后将 ( 入栈保存,将 ) 入栈匹配前一个元素是否是 ( ,如果是,则 pop() 前一个元素 (,如果不是,则 push() 这个 ) 入栈,最终查看栈是否为空,若是则检验成功,否则失败。

方法1:使用前面定义的 Stack 类

这里使用前面题目中定义的 Stack 类。

/**
 * 检验圆括号顺序的有效性
 * @param {String} str 
 */
function validParentheses (str){
  if(!str || str.length === 0 || str[0] === ')') return false

  let stack = new Stack()
  str.split('').forEach(char => {
    let status = stack.peek() === '(' && char === ')'
    status ? stack.pop() : stack.push(char)
  })
  return stack.isEmpty()
}

方法2:出入栈操作

/**
 * 检验圆括号顺序的有效性
 * @param {String} str 
 */
function validParentheses (str){
  if(!str || str.length === 0 || str[0] === ')') return false

  let arr = []
  for(let i = 0; i < str.length ; i++){
    str[i] === '(' ? arr.push(str[i]) : arr.pop()
  }
  return arr.length === 0
}

五、改造题二,添加一个 min 函数来获得栈中最小元素

步骤 数据栈 辅助栈 最小值
1.push 3 3 0 3
2.push 4 3, 4 0, 0 3
3.push 2 3, 4, 2 0, 0, 2 2
4.push 1 3, 4, 2 ,1 0, 0, 2, 3 1
5.pop 3, 4, 2 0, 0, 2 2
6.pop 3, 4 0, 0 3
7.push 3, 4 ,0 0, 0, 2 0

使用示例如下:

let stack = new Stack();
stack.push(3);
console.log('After push 3, Min item is', stack.min());
stack.push(4);
console.log('After push 4, Min item is', stack.min());
stack.push(2);
console.log('After push 2, Min item is', stack.min());
stack.push(1);
console.log('After push 1, Min item is', stack.min());
stack.pop();
console.log('After pop, Min item is', stack.min());
stack.pop();
console.log('After pop, Min item is', stack.min());
stack.push(0);
console.log('After push 0, Min item is', stack.min());

提示:利用辅助栈(Web 端可利用数组),每次对栈 push/pop 元素时,也同时更新辅助栈(存储最小元素的位置)

方法1:小操作

class Stack {
 constructor() {
  this.items = [];
  this.minIndexStack = [];
 }

 push(element) {
  this.items.push(element);
  let minLen = this.minIndexStack.length;
  let minItemIndex = this.minIndexStack[minLen - 1];
  if(minLen === 0 || this.items[minItemIndex] > item) {
   this.minIndexStack.push(this.items.length - 1);
  } else {
   this.minIndexStack.push(minItemIndex);
  }
 }

 pop() {
  this.minIndexStack.pop();
  return this.items.pop();
 }
 
 min() {
  let len = this.minIndexStack.length;
  return (len > 0 && this.items[this.minIndexStack[len - 1]]) || 0;
 }

 peek() {
  return this.items[this.items.length - 1];
 }
 
 // 省略其它方法
}

方法2:与方法1中push实现的差异

class Stack {
  constructor (){
    this.items = [] // 数据栈
    this.arr = []  // 辅助栈
  }
  push( element ){
    this.items.push(element)
    let min = Math.min(...this.items)
    this.arr.push( min === element ? this.size() - 1 : 0)
  }
  pop(){
    this.arr.pop()
    return this.items.pop()
  }
  peek(){
    return this.items[this.items.length - 1]
  }
  isEmpty(){
    return this.items.length === 1
  }
  clear(){
    this.items = []
  }
  size(){
    return this.items.length
  }
  min (){
    let last = this.arr[this.arr.length - 1]
    return this.items[last]
  }
}

以上所述是小编给大家介绍的数据结构与算法(Stack)详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
基于jQuery的Spin Button自定义文本框数值自增或自减
Jul 17 Javascript
jQuery.fn和jQuery.prototype区别介绍
Oct 05 Javascript
jquery数组封装使用方法分享(jquery数组遍历)
Mar 25 Javascript
JavaScript DOM元素尺寸和位置
Apr 13 Javascript
一张Web前端的思维导图分享
Jul 03 Javascript
JS Array.slice 截取数组的实现方法
Jan 02 Javascript
JS作为值的函数用法示例
Jun 20 Javascript
js绘制购物车抛物线动画
Nov 18 Javascript
JS非空验证及邮箱验证的实例
Aug 11 Javascript
9种改善AngularJS性能的方法
Nov 28 Javascript
一文读懂ES7中的javascript修饰器
May 06 Javascript
JavaScript中展开运算符及应用的实例代码
Jan 14 Javascript
在vue中获取微信支付code及code被占用问题的解决方法
Apr 16 #Javascript
JavaScript实现选项卡效果的分析及步骤
Apr 16 #Javascript
Vuex持久化插件(vuex-persistedstate)解决刷新数据消失的问题
Apr 16 #Javascript
详解滑动穿透(锁body)终极探索
Apr 16 #Javascript
一些手写JavaScript常用的函数汇总
Apr 16 #Javascript
浏览器事件循环与vue nextTicket的实现
Apr 16 #Javascript
理理Vue细节(推荐)
Apr 16 #Javascript
You might like
雄兵连:第三季确定会出,不过时间未定,鹤熙是第三季的主角!
2020/03/13 国漫
异步加载技术实现当滚动条到最底部的瀑布流效果
2014/09/16 PHP
PHP中使用addslashes函数转义的安全性原理分析
2014/11/03 PHP
PHP数组操作简单案例分析
2016/10/15 PHP
广告切换效果(缓动切换)
2009/05/27 Javascript
js遍历td tr等html元素
2012/12/13 Javascript
NODE.JS加密模块CRYPTO常用方法介绍
2014/06/05 Javascript
jQuery使用之设置元素样式用法实例
2015/01/19 Javascript
详解JavaScript中双等号引起的隐性类型转换
2016/05/30 Javascript
jQuery+ajax实现滚动到页面底部自动加载图文列表效果(类似图片懒加载)
2016/06/07 Javascript
对js中回调函数的一些看法
2016/08/29 Javascript
浅谈JavaScript 覆盖原型以及更改原型
2016/08/31 Javascript
AngularJS Toaster使用详解
2017/02/24 Javascript
JS+canvas动态绘制饼图的方法示例
2017/09/12 Javascript
浅谈jquery中ajax跨域提交的时候会有2次请求的问题
2017/11/10 jQuery
Node.js中sequelize时区的配置方法
2017/12/10 Javascript
vue中使用echarts制作圆环图的实例代码
2018/07/27 Javascript
AngularJs1.x自定义指令独立作用域的函数传入参数方法
2018/10/09 Javascript
node使用Mongoose类库实现简单的增删改查
2018/11/08 Javascript
微信小程序实现购物页面左右联动
2019/02/15 Javascript
vue cli3.0结合echarts3.0与地图的使用方法示例
2019/03/26 Javascript
vue实现输入框的模糊查询的示例代码(节流函数的应用场景)
2019/09/01 Javascript
vue实现商品列表的添加删除实例讲解
2020/05/14 Javascript
从Python程序中访问Java类的简单示例
2015/04/20 Python
python对离散变量的one-hot编码方法
2018/07/11 Python
python读取指定字节长度的文本方法
2019/08/27 Python
python实现淘宝购物系统
2019/10/25 Python
基于python检查SSL证书到期情况代码实例
2020/04/04 Python
详解Sticky Footer 绝对底部的两种套路
2017/11/03 HTML / CSS
大三在校生电子商务求职信
2013/10/29 职场文书
家长建议怎么写
2014/05/15 职场文书
岁月神偷观后感
2015/06/11 职场文书
幼儿园教师培训心得体会
2016/01/21 职场文书
公司要求试用期员工提交“述职报告”,该怎么写?
2019/07/17 职场文书
利用 SQL Server 过滤索引提高查询语句的性能分析
2021/07/15 SQL Server
nginx实现多geoserver服务的负载均衡
2022/05/15 Servers