栈是一种遵循后进先出(ILFO)原则的有序集合,新添加或待删除的元素都保存在栈的同一段,称为栈顶,另一端就叫栈底。现实中很多例子采用了这种数据结构,比如一摞书,叠放的盘子。栈通常用来保存变量、方法调用,当然也可以用于浏览器历史记录。
基于数组的栈
创建类
class StackArray {
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 = [];
}
转化为数组
toArray() {
return this.items;
}
转化为字符串
toString() {
return this.items.toString();
}
基于Javascript对象的stack类
创建stack类
class Stack {
constructor() {
this.count = 0;//记录栈的大小,帮助我们进行添加和删除操作
this.items = {};
}
添加元素
push(element) {
this.items[this.count] = element;
this.count++;
}
删除元素
pop() {
if (this.isEmpty()) {
return undefined;
}
this.count--;
const result = this.items[this.count];
delete this.items[this.count];
return result;
}
验证一个栈是否为空和它的大小
isEmpty() {
return this.count === 0;
}
size() {
return this.count;
}
查看栈顶的值并将栈清空
peek() {
if (this.isEmpty()) {
return undefined;
}
return this.items[this.count - 1];
};
clear() {
/* while (!this.isEmpty()) {
this.pop();
} */
this.items = {};
this.count = 0;
};
转为字符串
toString() {
if (this.isEmpty()) {
return '';
}
let objString = `${this.items[0]}`;
for (let i = 1; i < this.count; i++) {
objString = `${objString},${this.items[i]}`;
}
return objString;
}
}
总结
除了toString方法,我们创建的其他方法的时间复杂度均是O(1),代表我们可以直接扎到目标元素并对其进行操作。
注意
目前为止我们使用的是ES6语法类,由于JavaScript语言的特点,类并不能跟其他语言一样具有私有属性,也就是在外部可以引用私有属性,对其进行赋值等操作。看以下的代码了解一下:
const stack = new Stack();
console.log(Object.getOwnPropertyNames(stack));//["count","items"]
console.log(Object.keys(stack));//["count","items"]
console.log(stack.items);//直接访问私有属性
那么问题来了,我们只想让用户使用我们暴露出来的属性或者方法,我们该怎么实现私有属性呢。笔者看了前辈总结的,直接把它写上来。
保护数据结构内部元素
下划线命名约定
class stack{
constructor(){
this._count = 0;
this._items = {};
}
};
这只是一种约定,只能依赖于开发人员具备的常识
用E2015的限定作用于Symbol实现类
const _items = Symbol('stackItems');
class Stack{
constructor(){
this[_items] = [];
}
};
实现了假的私有属性,虽然Symbol基本类型不可变,但由于ES2015新增的Object.getOwnPropertySymbols方法仍然能取到所有Symbol属性,而且是数组的形式,但我们操作的是栈,不应该出现这种行为。别急,还有第三个方案
用ES2015的WeakMap实现类
const items = new WeakMap();
class Stack{
constructor(){
items.set(this,[]);
},
push(element){
const s = items.get(this);
s.push(element);
}
}
这样我们就实现了一个实现了一个完全的私有属性 ,但是代码可读性对于笔者真的太难受了,不知道你们怎么觉得呢。
栈的实践
进制转换算法
function baseConverter(decNumber, base) {
const remStack = new Stack();
const digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
let number = decNumber;
let rem;
let baseString = '';
if (!(base >= 2 && base <= 36)) {
return '';
}
while (number > 0) {
rem = Math.floor(number % base);
remStack.push(rem);
number = Math.floor(number / base);
}
while (!remStack.isEmpty()) {
baseString += digits[remStack.pop()];
}
平衡圆括号算法
function parenthesesChecker(symbols) {
const stack = new Stack();
const opens = '([{';
const closers = ')]}';
let balanced = true;
let index = 0;
let symbol;
let top;
while (index < symbols.length && balanced) {
symbol = symbols[index];
if (opens.indexOf(symbol) >= 0) {
stack.push(symbol);
} else if (stack.isEmpty()) {
balanced = false;
} else {
top = stack.pop();
if (!(opens.indexOf(top) === closers.indexOf(symbol))) {
balanced = false;
}
}
index++;
}
return balanced && stack.isEmpty();
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。