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

684 阅读4分钟

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

介绍

队列是计算机科学常用的数据结构,当然我们的生活中也是的,它遵循先进先出的原则,比如安排任务就会说加入任务队列,再比如网页的新手引导队列,本期我们就会用普通对象的形式而非数组的形式,手写一个队列的类出来,加深我们的理解。

概念

队列是遵循先进先出(FIFO,也称为先来先服务)原则的一组有序的项。队列在尾部添加新元素,并从顶部移除元素。最新添加的元素必须排在队列的末尾。

正文

1.基础结构

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

const items = new WeakMap();
const count = new WeakMap();
const delCount = new WeakMap();
class Queue {
    constructor() {
        items.set(this, {})
        count.set(this, 0)
        delCount.set(this, 0)
        return this;
    }
    size() {}
    get(){}
    enqueue(item) { }
    dequeue() {}
    peak() {}
    clear() {}
}
export default Queue;

我们使用WeakMap这个ES6新数据结构来完成队列的私有变量了,保护其内部的参数不被外界破坏。我们在这里创建三个私有变量分别为items元素存储器与count计数器还有delCount删除收集计数器,至于后面的几个方法为:

  • size:返回当前队列的元素个数。

  • get:返回一个当期队列所有元素和长度值。

  • enqueue:向队列尾部添加一个元素,返回其当前。

  • dequeue:移除队列的第一项,并返回其元素。

  • peek:返回队列中第一个元素。

  • clear:清除队列所有的元素。

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

2.size&get&clear

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

size这里去做队列的长度计算,因为队列是先进先出的的,移走后始终会出现缺省值,比如下标为0一旦移走了,那么下标为0的永远没有了,下次进入队列的值就是1做下标了,那么长度就不能单纯依靠计数器count去完成了,所以我们期望当一个元素从队列移除后,那么delCount做计数加一,再让两个计数器做差,这样就能得到整个队列的长度了。这个可以往后对比dequeue方法去观察。

至于,get方法,我们仅仅去为了方便我们本次查看元素的变化,所以返回了元素和长度两个信息。clear为完全情况当前的元素和计数器,这里也不做赘述。

3.enqueue

enqueue(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 this.size();
}

enqueue是向队列尾部添加一个元素,类似于数组中的push。将传入的元素注入到元素集合中,同时改变计数器。并用刚才size方法获取并返回当前队列长度。

4.dequeue&peek

dequeue() {
    if (this.size() == 0) return undefined;
    let _items = items.get(this);
    let _delcount = delCount.get(this);
    let item = _items[_delcount];
    delete _items[_delcount];
    delCount.set(this,_delcount+1)
    return item;
}

peak() {
    if (this.size() == 0) return undefined;
    return items.get(this)[delCount.get(this)];
}

dequeue和peak都是拿他第一个元素,我们用delCount去拿取,如果当前没有元素移除他自然就是0,每当移除一个就要加一,那么队列第一个值得下标就是他delCount的当前值了。

5.使用&结果

        import Queue from "./js/Queue"
        const queue= new Queue();
        console.log('enqueue->',queue.enqueue(1));
        console.log('enqueue->',queue.enqueue(2))
        console.log('enqueue->',queue.enqueue(3))
        console.log('get->',queue.get());

        console.log("---------------")

        console.log('peak->',queue.peak())
        console.log('get->',queue.get());

        console.log("---------------")

        console.log('dequeue->',queue.dequeue())
        console.log('get->',queue.get());


        console.log("---------------")

        console.log('clear->',queue.clear());
        console.log('get->',queue.get());
  • 'enqueue推入队列三条数据,看看数据的变化。

  • peak方法查看当前队列的首个元素。

  • dequeue方法移除队列里首个元素,并返回,与peak作对比看数据变化。

  • clear情况数据,观察数据变化。

微信截图_20211110213423.png

我们发现结果都是符合期望的,这样我们就用js普通对象模拟出了一个受保护的队列,遵循了先进先出的原则。

结语

本次我们学习实现了队列这一最基础的数据结构。其实队列在我们生活中也极为常见,比如在学校,电影院,门禁打卡,打印文件,我们都会排队。排在第一位的人会先接受服务,然后一个一个的进行服务,直到服务结束。在前端应用里,有这种业务,比如好多弹框业务,或者引导业务,都可以使用它或它的思想。