>[success] # 实现一个js的栈
~~~
1.上个章节我们说了一个实现思路是通过'数组形式'来实现一个栈,这种栈叫做'顺序栈',
这个章节就用js 实现一个顺序栈
~~~
>[info] ## 实现是一个顺序栈 -- 先进后出(LIFO 原则)
~~~
1.首先'顺序栈' 是基于数组,只能对栈顶进行操作因此接下来要实现的顺序栈会有以下的方法
push(element(s)):添加一个(或几个)新元素到栈顶。
pop():移除栈顶的元素,同时返回被移除的元素。
peek():返回栈顶的元素,不对栈做任何修改(该方法不会移除栈顶的元素,仅仅返回它)。
isEmpty():如果栈里没有任何元素就返回 true,否则返回 false。
clear():移除栈里的所有元素。
size():返回栈里的元素个数。该方法和数组的 length 属性很类似。
~~~
* 栈顶如图
![](https://img.kancloud.cn/19/01/1901c976e71669b1ae335a5554114f5d_129x164.png)
>[danger] ##### 代码实现
~~~
class StackArray{
constructor(){
// 创建一个数组用来保存栈
this.item = []
}
// 栈顶添加
push(element){
this.item.push(element)
}
// 移除栈顶元素
pop(){
return this.item.pop()
}
// 返回当前栈顶元素
peek(){
return this.item[this.item.length-1]
}
// 判断当前栈是否有元素
isEmpty(){
return this.item.length === 0
}
// 移除栈里所有元素
clear(){
this.item = []
}
// 返回栈中的个数
size(){
return this.item.length
}
toArray() {
return this.items;
}
toString() {
return this.items.toString();
}
}
~~~
>[info] ## 使用js 对象为基础实现 -- 栈
~~~
1.数组是连续的开辟内存空间所以对空间的占用相对较多,减少较少的
空间使用可以考虑对象这种创建方式
~~~
>[danger] ##### 代码实现
~~~
class Stack{
constructor(){
this.count = 0
this.items = {}
}
// 检查栈是否为空
isEmpty(){
return this.count === 0
}
// 添加元素
push(element){
this.items[this.count ++ ] = element
}
// 删除元素
pop(){
// 如果栈为空删除返回undefined
if(this.isEmpty()){
return undefined
}
const result = this.items[--this.count ]
delete this.items[this.count]
return result
}
// 返回栈顶元素
peek() {
if (this.isEmpty()) {
return undefined;
}
return this.items[this.count - 1];
}
// 栈的大小
size(){
return this.count
}
// 清楚栈
clear() {
// 循环这种 利用先进后出按顺序依次删除
/* while (!this.isEmpty()) {
this.pop();
} */
// 这种最简单粗暴一步到位
this.items = {};
this.count = 0;
}
toString() {
if (this.isEmpty()) {
return '';
}
// 为了可以拼接处1,2,3
let objString = `${this.items[0]}`;
for (let i = 1; i < this.count; i++) {
objString = `${objString},${this.items[i]}`;
}
return objString;
}
}
const stack = new Stack()
stack.push(1)
stack.push(2)
stack.push(3)
console.log(stack.toString()) // 1,2,3
~~~
>[info] ## 上面的两种实现的弊端
~~~
1.上面的两种已经实现了,但是不是那么友好,因为对外暴露了items,
这样使用的人可以任意对暴露的items 进行更改es10是具备私有属性,
我们可以将这些不想对外暴露的属性设置为私有属性
2.当然我们也可用'symbol'来操作,但这依旧会有问题
~~~
>[danger] ##### 使用symbol 创建私有属性
~~~
const _items = Symbol('stackItems');
class Stack {
constructor () {
this[_items] = [];
}
// 栈的方法
}
// 但是依旧可以使用 getOwnPropertySymbols 获取symbol 并且更改
const stack = new Stack();
stack.push(5);
stack.push(8);
let objectSymbols = Object.getOwnPropertySymbols(stack);
console.log(objectSymbols.length); // 输出 1
console.log(objectSymbols); // [Symbol()]
console.log(objectSymbols[0]); // Symbol()
stack[objectSymbols[0]].push(1);
stack.print(); // 输出 5, 8, 1
~~~
>[danger] ##### 使用 WeakMap
* WeakMap配合 数组
~~~
1.采用这种方法,代码的可读性不强,而且在扩展该类时无法继承私有属性
~~~
~~~
const items = new WeakMap();
class Stack {
constructor () {
items.set(this, []);
}
push(element){
const s = items.get(this);
s.push(element);
}
pop(){
const s = items.get(this);
const r = s.pop();
return r;
}
// 其他方法
}
~~~
* WeakMap配合 对象
~~~
const _items = new WeakMap();
const _count = new WeakMap();
class Stack {
constructor() {
_count.set(this, 0);
_items.set(this, {});
}
push(element) {
const items = _items.get(this);
const count = _count.get(this);
items[count] = element;
_count.set(this, count + 1);
}
pop() {
if (this.isEmpty()) {
return undefined;
}
const items = _items.get(this);
let count = _count.get(this);
count--;
_count.set(this, count);
const result = items[count];
delete items[count];
return result;
}
peek() {
if (this.isEmpty()) {
return undefined;
}
const items = _items.get(this);
const count = _count.get(this);
return items[count - 1];
}
isEmpty() {
return _count.get(this) === 0;
}
size() {
return _count.get(this);
}
clear() {
/* while (!this.isEmpty()) {
this.pop();
} */
_count.set(this, 0);
_items.set(this, {});
}
toString() {
if (this.isEmpty()) {
return '';
}
const items = _items.get(this);
const count = _count.get(this);
let objString = `${items[0]}`;
for (let i = 1; i < count; i++) {
objString = `${objString},${items[i]}`;
}
return objString;
}
}
~~~
>[info] ## 总结
~~~
1.当然了可以使用es10 私有属性或者使用ts,毕竟每个语言都有自己的
特点
~~~
- 接触数据结构和算法
- 数据结构与算法 -- 大O复杂度表示法
- 数据结构与算法 -- 时间复杂度分析
- 最好、最坏、平均、均摊时间复杂度
- 基础数据结构和算法
- 线性表和非线性表
- 结构 -- 数组
- JS -- 数组
- 结构 -- 栈
- JS -- 栈
- JS -- 栈有效圆括号
- JS -- 汉诺塔
- 结构 -- 队列
- JS -- 队列
- JS -- 双端队列
- JS -- 循环队列
- 结构 -- 链表
- JS -- 链表
- JS -- 双向链表
- JS -- 循环链表
- JS -- 有序链表
- 结构 -- JS 字典
- 结构 -- 散列表
- 结构 -- js 散列表
- 结构 -- js分离链表
- 结构 -- js开放寻址法
- 结构 -- 递归
- 结构 -- js递归经典问题
- 结构 -- 树
- 结构 -- js 二搜索树
- 结构 -- 红黑树
- 结构 -- 堆
- 结构 -- js 堆
- 结构 -- js 堆排序
- 结构 -- 排序
- js -- 冒泡排序
- js -- 选择排序
- js -- 插入排序
- js -- 归并排序
- js -- 快速排序
- js -- 计数排序
- js -- 桶排序
- js -- 基数排序
- 结构 -- 算法
- 搜索算法
- 二分搜索
- 内插搜索
- 随机算法
- 简单
- 第一题 两数之和
- 第七题 反转整数
- 第九题 回文数
- 第十三题 罗马数字转整数
- 常见一些需求
- 把原始 list 转换成树形结构