【数据结构与算法】用JS普通对象手撸一个栈

223 阅读3分钟

这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战

介绍

栈在编程语言的编译器和内存中保存变量、方法调用等,也被用于浏览器历史记录,是一种在底层比较常用的数据结构,而在js里因为存在好用的数组所代替。但是我们还是要对他进行了解,本期我们就会用普通对象的形式而非数组的形式,手写一个栈的类出来,加深我们的理解。

概念

栈是一种遵从后进先出(LIFO)原则的有序集合。新添加或待删除的元素都保存在栈的同一端,称作栈顶,另一端就叫栈底。在栈里,新元素都靠近栈顶,旧元素都接近栈底。

正文

1.基础结构

我们先看一下,要实现的大体结构与其方法:

const items = new WeakMap();
const count = new WeakMap();
class Stack {
    constructor() {
        items.set(this, {})
        count.set(this, 0)
        return this;
    }
    push(item) {}
    pop() {}
    peak() {}
    clear() {}
    size() {}
}
export default Stack;

相信很多同学最先看到的是WeakMap这个ES6新数据结构,我可以知道WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名,这样的话,最适合做私有变量了,正是因为保护其内部的参数不被外界破坏,我们就可以用WeakMap去创建出来,当然方法也不唯一的比如Symbol也可以是一个私有变量的实现方案。

好了言归正传,我们创建两个私有变量分别为items元素存储器与count计数器,至于后面的几个方法为:

  • push:添加一个新元素到栈顶。
  • pop:移除栈顶的元素,同时返回被移除元素的值。
  • peek:返回栈顶的元素。
  • clear:清除所有栈内的元素。
  • size:返回当前栈的元素个数。

接下来我们逐个说一下,怎么来实现。

2.push方法

push(item) {
    let _items = items.get(this);
    let _count = count.get(this);
    _items[_count] = item;
    _count += 1;
    items.set(this, _items);
    count.set(this, _count);
    return _count;
}

与数组类似,将传入的元素注入到元素集合中,同时改变计数器。当然这里为了方便计数,我们最后一个元素视为栈顶,第一个元素为栈底。

3.pop&peak

pop() {
    let _items = items.get(this);
    let _count = count.get(this);
    let item = _items[_count-1]
    delete _items[_count-1]; 
    _count -= 1;
    items.set(this, _items);
    count.set(this, _count);
    return item;
}
peak() {
    let _items = items.get(this);
    let _count = count.get(this);
    return _items[_count-1];
}

pop为从栈顶拿取并删除这个元素,而peak则不需要删除,但他们都会返回栈顶这个元素。

4.clear&size

clear() {
    items.set(this, {});
    count.set(this, 0);
}
size() {
    return count.get(this);
}

clear清空与size获取长度都很好理解,一个是将元素集合赋空将计数器置零,一个是返回当前计数器的当前值。

5.使用&结果

import Stack from "./js/Stack"
const stack= new Stack();
console.log('push->',stack.push(1));
console.log('push->',stack.push(2))
console.log('push->',stack.push(3))
console.log('pop->',stack.pop());
console.log('peak->',stack.peak());
console.log('clear->',stack.clear())
console.log('size->',stack.size())

我们先推送了123三个值进去,然后分别操作他的pop,peak,clear,size试试看。

微信截图_20211109203234.png

我们发现结果都是符合期望的,这样我们就用js普通对象模拟出了一个受保护的栈。

结语

本次我们学习实现了栈这一数据结构。其实栈的实际应用非常广泛。在回溯问题中,它可以存储访问过的任务或路径、撤销的操作。而且在Java和C#用栈来存储变量和方法调用,特别是处理递归算法时,还会抛出一个栈溢出异常,当然这只是冰山一角。数据结构是学习程序的基本功,还有更多的知识等着我们去探索呢~