欢迎查看我的 个人主页 && 个人博客 && 个人知识库 && 微信公众号“前端自习课”

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

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

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

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

1.Stack - 图1

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

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

方法1:ES6实现

  1. class Stack {
  2. constructor (){
  3. this.items = []
  4. }
  5. push( element ){
  6. this.items.push(element)
  7. }
  8. pop(){
  9. return this.items.pop()
  10. }
  11. peek(){
  12. return this.items[this.items.length - 1]
  13. }
  14. isEmpty(){
  15. return this.items.length === 0
  16. }
  17. clear(){
  18. this.items = []
  19. }
  20. size(){
  21. return this.items.length
  22. }
  23. }

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

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

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

方法3:使用 ES6 的 WeakMap 实现
知识点复习:ES6 中的 WeakMap 介绍

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

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

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

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

方法1:使用前面定义的 Stack 类
这里使用前面题目中定义的 Stack 类。

  1. /**
  2. * 十进制转换为二进制
  3. * @param {Number} bit
  4. */
  5. function bitset (bit){
  6. if(bit == 0) return '0'
  7. if(!/^[0-9]+.?[0-9]*$/.test(bit)){
  8. return new Error('请输入正确的数值!')
  9. }
  10. let stack = new Stack(), result = ''
  11. while (bit > 0){
  12. stack.push(bit % 2)
  13. bit = Math.floor(bit / 2)
  14. }
  15. while (!stack.isEmpty()){
  16. result += stack.pop().toString()
  17. }
  18. return result
  19. }

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

  1. /**
  2. * 十进制转换为二进制
  3. * @param {Number} bit
  4. */
  5. function bitset (bit){
  6. if(bit == 0) return '0'
  7. if(!/^[0-9]+.?[0-9]*$/.test(bit)){
  8. return new Error('请输入正确的数值!')
  9. }
  10. let arr = []
  11. while(bit > 0){
  12. arr.push(bit % 2)
  13. bit = Math.floor(bit / 2)
  14. }
  15. return arr.reverse().join('')
  16. }

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

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

《678. 有效的括号字符串》 https://leetcode-cn.com/problems/bracket-lcci/
主要目的就是:该函数接收一个圆括号字符串,判断里面的括号顺序是否有效,如果有效则返回 true 反之 false
如:

  • ( -> false
  • () -> true
  • (() -> false
  • ()) -> false
  • ()) -> false
  • (((()()))()) -> true

类似还有:《20. 有效的括号》

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

方法1:使用前面定义的 Stack 类
这里使用前面题目中定义的 Stack 类。

  1. /**
  2. * 检验圆括号顺序的有效性
  3. * @param {String} str
  4. */
  5. function validParentheses (str){
  6. if(!str || str.length === 0 || str[0] === ')') return false
  7. let stack = new Stack()
  8. str.split('').forEach(char => {
  9. let status = stack.peek() === '(' && char === ')'
  10. status ? stack.pop() : stack.push(char)
  11. })
  12. return stack.isEmpty()
  13. }

方法2:出入栈操作

  1. /**
  2. * 检验圆括号顺序的有效性
  3. * @param {String} str
  4. */
  5. function validParentheses (str){
  6. if(!str || str.length === 0 || str[0] === ')') return false
  7. let arr = []
  8. for(let i = 0; i < str.length ; i++){
  9. str[i] === '(' ? arr.push(str[i]) : arr.pop()
  10. }
  11. return arr.length === 0
  12. }

五、改造题二,添加一个 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

使用示例如下:

  1. let stack = new Stack();
  2. stack.push(3);
  3. console.log('After push 3, Min item is', stack.min());
  4. stack.push(4);
  5. console.log('After push 4, Min item is', stack.min());
  6. stack.push(2);
  7. console.log('After push 2, Min item is', stack.min());
  8. stack.push(1);
  9. console.log('After push 1, Min item is', stack.min());
  10. stack.pop();
  11. console.log('After pop, Min item is', stack.min());
  12. stack.pop();
  13. console.log('After pop, Min item is', stack.min());
  14. stack.push(0);
  15. console.log('After push 0, Min item is', stack.min());

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

方法1:小操作

  1. class Stack {
  2. constructor() {
  3. this.items = [];
  4. this.minIndexStack = [];
  5. }
  6. push(element) {
  7. this.items.push(element);
  8. let minLen = this.minIndexStack.length;
  9. let minItemIndex = this.minIndexStack[minLen - 1];
  10. if(minLen === 0 || this.items[minItemIndex] > item) {
  11. this.minIndexStack.push(this.items.length - 1);
  12. } else {
  13. this.minIndexStack.push(minItemIndex);
  14. }
  15. }
  16. pop() {
  17. this.minIndexStack.pop();
  18. return this.items.pop();
  19. }
  20. min() {
  21. let len = this.minIndexStack.length;
  22. return (len > 0 && this.items[this.minIndexStack[len - 1]]) || 0;
  23. }
  24. peek() {
  25. return this.items[this.items.length - 1];
  26. }
  27. // 省略其它方法
  28. }

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

  1. class Stack {
  2. constructor (){
  3. this.items = [] // 数据栈
  4. this.arr = [] // 辅助栈
  5. }
  6. push( element ){
  7. this.items.push(element)
  8. let min = Math.min(...this.items)
  9. this.arr.push( min === element ? this.size() - 1 : 0)
  10. }
  11. pop(){
  12. this.arr.pop()
  13. return this.items.pop()
  14. }
  15. peek(){
  16. return this.items[this.items.length - 1]
  17. }
  18. isEmpty(){
  19. return this.items.length === 1
  20. }
  21. clear(){
  22. this.items = []
  23. }
  24. size(){
  25. return this.items.length
  26. }
  27. min (){
  28. let last = this.arr[this.arr.length - 1]
  29. return this.items[last]
  30. }
  31. }

下周预告

下周将练习队列(Queue) 的题目,开始翻起算法书籍学习咯。

Author 王平安
E-mail pingan8787@qq.com
博 客 www.pingan8787.com
微 信 pingan8787
每日文章推荐 https://github.com/pingan8787/Leo_Reading/issues
ES小册 js.pingan8787.com

微信公众号

1.Stack - 图2