栈是一种遵从后进先出LIFO(Last In First Out)
原则的有序集合。新添加或待删除的元素都保存在栈的同一端,称作栈顶,另一端就叫栈底。在栈里,新元素都靠近栈顶,旧元素都接近栈底。
生活中的案例
- 堆成山的书
- 堆成山的碗具
这些案例中所有的特点都是添加的时候放在最顶端,移除的时候都是从顶端开始移除。
创建一个 Stack
接下来 我们用 JavaScript
这门语言来描述 栈
这种数据解构。
class Stack {
constructor() {
this.items = {};
}
}
我们使用了 JavaScript 的 对象 来 储存栈结构,接下来我们需要遵循(LIFO)原则,对元素的添加和删除做一些限制。
Stack 拥有的方法?
push(element)
:添加一个 或 几个新元素 到栈顶。pop()
:移除栈顶的元素,并返回移除的元素。peek()
: 返回栈顶的元素,不做任何改变。isEmpty()
:如果栈里没有任何元素就返回true
,反之返回false
。clear()
:移除栈里所有的元素。size()
: 返回栈里的元素个数,和 数组的length
属性相似。toString()
: 返回当前栈所有元素的内容。
class Stack {
constructor() {
this.items = {}; // 储存元素
this.count = 0; // 记录栈元素的个数
}
// methods
push(element) {
this.items[this.count] = element;
this.count++;
}
pop() {
if (this.isEmpty()) {
return undefined;
}
this.count--;
const current = this.items[this.count];
delete this.items[this.count];
return current;
}
peek() {
if (this.isEmpty()) {
return undefined;
}
return this.items[this.count - 1];
}
clear() {
this.items = {};
this.count = 0;
// or
// while (!this.isEmpty()) {
// this.pop()
// }
}
isEmpty() {
return this.count === 0;
}
toString() {
if (this.isEmpty()) {
return "";
}
let str = `${this.items[0]}`;
let index = 1;
while (index < this.count) {
str = `${str},${this.items[index]}`;
index++;
}
return str;
}
}
操作
const stack = new Stack();
stack.push(1); // 1
stack.push("a"); // 1,a
stack.peek(); // a
stack.pop(); // a
stack.toString(); // 1
问题
在 JavaScript 里,这个stack
实例可以直接访问到items
,count
。
const stack = new Stack();
stack.items = { 1: "a", 5: "c" };
我们希望保护内部的元素,只有我们暴露出的方法才能修改内部结构。不幸的是,我们在 Stack 类中声明的 items 和 count 属
性并没有得到保护,因为 JavaScript 的类就是这样工作的。
下划线命名约定
一部分开发者喜欢在 JavaScript 中使用下划线命名约定来标记一个属性为私有属性。
class Stack {
constructor() {
this._items = {};
this._count = 0;
}
}
用 ES2015 的限定作用域 Symbol 实现类
const _items = Symbol("stackItems");
class Stack {
constructor() {
this[_items] = {};
}
}
- 不过依然可以访问到这个属性
const _items = Symbol("stackItems");
class Stack {
constructor() {
this[_items] = {};
}
}
const stack = new Stack();
const symbolKeys = Object.getOwnPropertySymbols(stack); // [Symbol(stackItems)]
stack[symbolKeys[0]] = { 1: "a", 4: "5" };
stack.toString(); // a,5
使用栈来解决的问题
- JavaScript 执行栈
- 十进制转换
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()];
}
return baseString;
}
baseConverter(100, 8); // '144'
baseConverter(50, 2); // '110010'