JavaScript数据结构 - 队列

前言

若是你问我,人可否改变本身的命运,我也不晓得。但我晓得,不认命,就是咱们这代人的命。javascript

  1. 基础知识就像是一座大楼的地基,它决定了咱们的技术高度。
  2. 咱们应该多掌握一些可移值的技术或者再过十几年应该都不会过期的技术,数据结构与算法就是其中之一。

正文

队列被用在不少地方,好比提交操做系统执行的一系列进程、打印任务池等。java

队列(queue) 只容许在表的一端进行插入,而在另外一端进行删除元素。容许插入的一端称为 队尾(rear) ,容许删除的一端则称为 队头(front) 。队列中没有元素时称为 空队列node

队列的定义

  1. 队列是遵循 先进先出(FIFO) 原则的一组有序的线性结构。
  2. 队列在尾部添加新元素,在顶部移除元素。
  3. 插入通常称为 入队(enqueue) ,删除则称为 出队(dequeue)
  4. 最新添加的元素必须排在队列的末尾
  5. 和栈同样,队列是一种 操做受限 的线性表。
  6. 不包含任何元素的队列称为 空队列

队列的实现

从数据存储的角度看,实现队列有两种方式,一种是以数组作基础,一种是以链表作基础。算法

队列方法:数组

操做 方法
入队 enqueue(el)
出队 dequeue()
取队头元素 front()
判断是否为空队列 isEmpty()
返回队列大小 size()
清空队列 clear()

顺序队列

这里采用的是数组做为其存储数据的底层数据结构。数据结构

用数组 _data 保存队列内元素,构造函数将其初始化为一个空数组。函数

class Queue {
    constructor() {
        this._data = [];
    }
    enqueue(el) {   // 添加新元素到队尾
        this._data.push(el);
    }
    dequeue() {     // 移除队头元素,同时返回被移除的元素
        return this._data.shift();
    }
    front() {       // 查看队头元素
        return this._data[0];
    }
    isEmpty() {     // 判断是否为空队列
        return this._data.length === 0;
    }
    size() {        // 查询队列的大小
        return this._data.length;
    }
    clear() {
        this._data = [];
    }
    // 非必重写方法,仅供测试使用
    // 重写 JavaScript 对象默认的 toString() 方法
    // toString() {
    // return this._data.join(' -> ');
    // }
}
复制代码

链队

这里采用的是链表做为其存储数据的底层数据结构。测试

链队是指采用链式存储结构实现的队列。一般链队使用单链表来表示。this

function Queue() {
    // 这里使用 _front 指代头指针,_rear 指代尾指针
    this._front = this._rear = new this.__Node__('head');
    this._len = 0;
}
Queue.prototype = {
    constructor: Queue,
    __Node__: function(el) {
        this.data = el;
        this.next = null;
    },
    enqueue: function(el) {     // 添加新元素到队尾
        const __node__ = new this.__Node__(el);
        this._rear.next = __node__;
        this._rear = this._rear.next;
        this._len++;
    },
    dequeue: function() {       // 移除队头元素,同时返回被移除的元素
        if(this._len === 0) return;
        const __node__ = this._front.next;
        this._front.next = __node__.next;
        this._len--;
        return __node__.data;
    },
    front: function() {         // 查看队头元素
        return this._front.next && this._front.next.data;
    },
    isEmpty: function() {       // 判断是否为空队列
        return this._len === 0;
    },
    size: function() {          // 查询队列的大小
        return this._len;
    },
    clear: function() {
        this._front = this._rear = new this.__Node__('head');
        this._len = 0;
    },
    // 非必重写方法,仅供测试使用
    // 重写 JavaScript 对象默认的 toString() 方法
    // toString() {
    // let curr = this._front.next,
    // list = [];
    // while(curr) {
    // list.push(curr.data);
    // curr = curr.next;
    // }
    // return list.join(' -> ');
    // }
}
复制代码

上述顺序队列、链队测试用例:spa

const queue = new Queue();
queue.isEmpty();        // true
queue.enqueue('a');     // undefined
queue.enqueue('b');     // undefined
queue.enqueue('c');     // undefined
// queue.toString(); // "a -> b -> c"
queue.size();           // 3
queue.isEmpty();        // false
queue.dequeue();        // "a"
queue.dequeue();        // "b"
// queue.toString(); // "c"
queue.clear();          // undefined
queue.size();           // 0
复制代码

循环队列

传统的队列 中,底层数据结构使用的是数组进行存储的状况下(实际上,是使用了一组地址连续的存储单元依次存放从队列头到队列尾的元素),咱们须要指定队列所能达到的最大长度;在 出队 操做时,队列的 头指针(front) 指向数组的下一个元素(也就是下一段存储空间),会形成实际状况下,队列中的元素总和,没法达到队列的最大长度,咱们称这一现象为 假溢出 。而循环队列偏偏是解决假溢出的方案之一。

其实在实现顺序队列的时候,咱们已经借助 JavaScript内置的API 间接的实现了咱们 循环队列 的需求。

优先队列

通常状况下,从队列中删除的元素,必定是率先入队的元素。可是也有一些使用队列的应用,在删除元素时没必要遵循先进先出的约定。这种时候,就须要使用一种叫作 优先队列 的数据结构来进行处理。

咱们假设,在将元素入队时将 优先码 一并入队,而且优先码按从小到大进行排序,优先码越小,则表明优先级越高。下面简单讲 顺序队列 更改成 优先队列

<!-- 在这里咱们对上面的顺序列表进行稍微改写,使程序能知足咱们的需求 -->
class Queue {
    ...
    enqueue(el, code = 3) {   // 添加新元素到队尾
        const p = {
            val: el,
            code: code
        }
        let i = 0,
            added = true;
        while(i < this._data.length) {
            if(code <= this._data[i].code) {
                this._data.splice(i, 0, p);
                added = false;
                break;
            }
            i++
        }
        if(added) this._data.push(p);
    }
    dequeue() {     // 移除队头元素,同时返回被移除的元素
        return this._data.shift().val;
    }
    front() {       // 查看队头元素
        return this._data[0].val;
    }
    ...
    // 非必重写方法,仅供测试使用
    // 重写 JavaScript 对象默认的 toString() 方法
    // toString() {
    // const ret = this._data.map(item => {
    // return item.val;
    // });
    // return ret.join(' -> ');
    // }
}
复制代码

优先队列测试用例:

var queue = new Queue();
queue.isEmpty();        // true
queue.enqueue("a", 1);  // undefined
queue.enqueue("d", 3);  // undefined
queue.enqueue("b", 2);  // undefined
queue.enqueue("c", 3);  // undefined
queue.toString();       // "a -> b -> c -> d"
queue.size();           // 4
queue.isEmpty();        // false
queue.dequeue();        // "a"
queue.dequeue();        // "b"
queue.toString();       // "c -> d"
queue.clear();          // undefined
queue.size();           // 0
复制代码

结语

队列在程序设计中也常常出现。典型的例子就是操做系统中的做业排队。

若是有错误或者不严谨的地方,请务必给予指正,十分感谢。

相关文章
相关标签/搜索