堆栈是只能从列表的一端(称为顶部)访问的元素列表. 一个常见的,现实生活中的例子就是自助餐厅的托盘堆. 托盘老是从顶部取走, 当托盘在清洗后放回堆栈时,它们被放置在堆栈的顶部. 堆栈被称为后进先出(LIFO)数据结构.git
因为堆栈的后进先出特性, 当前不在栈顶都不能被访问的. 假如想访问栈底的元素必须把上面的元素移除(托盘取走)github
栈是一种类队列的数据结构, 能够用解决许多计算问题. 同时栈是很是高效的一种数据结构,因数据新增、删除都只能从栈顶完成, 而且容易以及快速实现.数组
因为栈后入先出的特性,任何非栈顶元素不能被访问. 要想访问栈底的元素必须把上面的元素所有出栈才能访问到.安全
下面栈的经常使用的两个操做,元素入栈、出栈. 使用push
操做将元素添加到堆栈中, 使用pop
操做从堆栈中取出元素. 下图所示数据结构
堆栈另外一个常见操做是查看堆栈顶部的元素. pop
操做访问堆栈的顶部元素,可是它将永久地从堆栈中删除该元素. peek
操做返回存储在堆栈顶部的值,而不从堆栈中删除它. 除pop()
、push()
、peek()
主要方法外, 栈应该包含其它的如判断栈是否是空 isEmpty()
、获取栈的大小 size()
、清空栈 clear()
.
下面经过代码来实现一个栈.测试
经过数组来实现一个栈, 代码以下:this
class Stack { constructor() { this.items = [] } push(ele) { this.items.push(ele) } pop() { return this.items.pop() } peek() { return this.items[this.items.length - 1] } isEmpty() { return !!this.items.length } size() { return this.items.length } clear() { this.items = [] } print(){ return this.items.toString() } }
下面经过一个案例来测试下定义栈类.spa
const stack = new Stack() console.log(stack.isEmpty()) //=> true stack.push(1) //=> 入栈 1 stack.push(2) //=> 入栈 2 stack.push(3) //=> 入栈 3 stack.pop() //=> 出栈 3 stack.pop() //=> 出栈 2 stack.push(4) //=> 入栈 4 console.log(stack.isEmpty()) //=> false console.log(stack.size()); //=> 2 stack.clear(); console.log(stack.isEmpty()); //=> true
上面代码只是简单操做,这里就不一一介绍了.code
下面经过几个实际的例子来增长咱们对栈的理解:blog
把一个数字从一个基数转为另一个基数. 给定一个数字 n
, 要把它转为基数b
, 下面大概的步骤:
进制转换,经过栈很是容易实现,下面大概实现方式
function baseConverter(num, base) { const stack = new Stack() const digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' if(!(base >= 2 && base <= 32)){ return ''; } do { stack.push(num % base) num = Math.floor(num / base) } while (num > 0) let converted = '' while (!stack.isEmpty()) { converted += digits[stack.pop()] } return converted; }
下面咱们来测试下: 把一个数字转为二进制、八进制
let num = 32 let base = 2 let converted = baseConverter(num, base) console.log(`${num} converted to base ${base} is ${converted}`) num = 125 base = 8 converted = baseConverter(num, base) console.log(`${num} converted to base ${base} is ${converted}`)
运行输出后结果为:
32 converted to base 2 is 100000
125 converted to base 8 is 175
什么是回文:
回文是先后拼写相同的单词、短语或数字。例如,“dad”是一个回文;“racecar”是一个回文; 1001是一个数字回文;
咱们可使用堆栈来肯定给定的字符串是不是回文. 拿到
下面经过代码实现判断回文的方法:
function isPalindrome(word) { const stack = new Stack() for (let i = 0; i < word.length; i++) { stack.push(word[i]) } let rword = '' while (!stack.isEmpty()) { rword += stack.pop() } return word === rword } console.log(isPalindrome('racecar')) // true console.log(isPalindrome('hello')) // false
注意
实现判断回文方式不少, 这里只是列举经过栈如何实现
演示如何用堆栈实现递归,就以阶乘为例.
5!= 5 * 4 * 3 * 2 * 1 = 120
先用递归方法实现, 具体以下:
function factorial(n) { if (n === 1) { return 1 } else { return n * factorial(n-1) } }
👇使用堆栈模拟递归过程:
function fact(n) { const stack = new Stack() while (n > 1) { stack.push(n--) } let product = 1 while (!stack.isEmpty()) { product *= stack.pop() } return product }
私有化实际就是隐藏内部细节,只对外提供操做方法,这样能保证代码安全性. 下面经常使用方式:
class Stack { constructor() { this._items = [] } }
这种方式只是一种约定并不能起到保护做用,并且只能依赖使用咱们代码的开发者所具有的常识.
const _items = Symbol('stackItems') class Stack { constructor() { this[_items] = [] } } //...
}
这种方式其实建立一个假的私有属性, ES2015新增的Object.getOwnPropertySymbols
方法能取到类里面声明的全部 Symbol
属性, 下面是破坏Stack类的例子
const stack = new Stack()\ stack.push(1) stack.push(2) let objectSymbols = Object.getOwnPropertySymbols(stack) console.log(objectSymbols.length) // 1 console.log(objectSymbols) // [ Symbol(stackItems) ] console.log(stack[objectSymbols[0]].push(1)) console.log(stack.print()) // 1,2,1
const items = new WeakMap() class Stack { constructor() { items.set(this, []) } push(ele) { const s = items.get(this) s.push(ele) } pop() { const s = items.get(this) const r = s.pop() return r } }
items
在 Stack 类里是真正的私有属性. 采用这种方式带来问题代码可读性不强,并且扩展该类时没法继承私有属性.
虽然在 TypeScrpit 中存在经过 private
修饰符来定义私有变量, 可是这只能编译时才有效,在编译后仍是能够被外部访问.
在ES草案中提供以下方式进行声明私有化属性
class Stack { #items = []; push(ele) { // 访问 this.#items.push(ele) } }