手写数据结构 队列篇

简介: 手写数据结构 队列篇

1. 队列简介

我们已经学习了栈。队列和栈非常类似,但是使用了与后进先出不同的原则。你将在本章学习这些内容。我们同样要学习双端队列的工作原理。双端队列是一种将栈的原则和队列的原则混合在一起的数据结构。

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

在现实中,最常见的队列的例子就是排队。在电影院、自助餐厅、杂货店收银台,我们都会排队。排在第一位的人会先接受服务。

在计算机科学中,一个常见的例子就是打印队列。比如说我们需要打印五份文档。我们会打开每个文档,然后点击打印按钮。每个文档都会被发送至打印队列。第一个发送到打印队列的文档会首先被打印,以此类推,直到打印完所有文档。

1.1. 实现

const Queue = (() => {
  const store = new WeakMap();
  class Queue {
    constructor() {
      store.set(this, { _items: [] });
    }
    // 只读 队列大小
    get size() {
      return store.get(this)._items.length;
    }
    // 只读 是否为空
    get isEmpty() {
      return this.size === 0;
    }
    // 进入队列
    enqueue(ele) {
      store.get(this)._items.push(ele);
      return this.size;
    }
    // 退出队列
    dequeue() {
      return store.get(this)._items.shift();
    }
    // 返回队列第 1 个元素
    peek() {
      return store.get(this)._items[0];
    }
    // 默认迭代器 用于展示所有元素
    [Symbol.iterator]() {
      let i = 0;
      return {
        next: () => ({ value: store.get(this)._items[i], done: i++ === this.size }),
        [Symbol.iterator]() {
          return this;
        },
      };
    }
    // 清空所有元素
    clear() {
      store.get(this)._items = [];
    }
  }
  return Queue;
})();

1.2. 使用

const queue = new Queue();
queue.size = 2;
console.log(queue.size);
// -> 0
console.log(queue);
// -> Queue {}
console.log(queue.enqueue('apple'));
// -> 1
console.log(queue.enqueue('orange'));
// -> 2
console.log(queue.enqueue('pear'));
// -> 3
console.log(...queue);
// -> 'apple' 'orange' 'pear'
console.log(queue.dequeue());
// -> 'apple'
console.log(queue.peek());
// -> 'orange'
console.log(queue.size);
// -> 2
queue.clear();
console.log(queue.isEmpty);
// -> true
console.log(queue.peek());
// -> undefined

2. 双端队列

双端队列(double-ended queue) 是一种允许我们同时从前端和后端添加和移除元素的特殊队列。

双端队列在现实生活中的例子有电影院、餐厅中排队的队伍等。举个例子,一个刚买了票的人如果只是还需要再问一些简单的信息,就可以直接回到队伍的头部。另外,在队伍末尾的人如果赶时间,他可以直接离开队伍。

在计算机科学中,双端队列的一个常见应用是存储一系列的撤销操作。每当用户在软件中进行了一个操作,该操作会被存在一个双端队列中(就像在一个栈里)。当用户点击撤销按钮时,该操作会被从双端队列中弹出,表示它被从后面移除了。在进行了预先定义的一定数量的操作后,最先进行的操作会被从双端队列的前端移除。由于双端队列同时遵守了先进先出和后进先出原则,可以说它是把队列和栈相结合的一种数据结构。

2.1. 实现

const Dequeue = (() => {
  const store = new WeakMap();
  class Dequeue {
    constructor() {
      store.set(this, { _items: [] });
    }
    // 只读 元素数量
    get size() {
      return store.get(this)._items.length;
    }
    // 只读 是否为空
    get isEmpty() {
      return this.size === 0;
    }
    // 在前面添加元素
    addFront(ele) {
      store.get(this)._items.unshift(ele);
      return this.size;
    }
    // 在前面移除元素
    removeFront() {
      return store.get(this)._items.shift();
    }
    // 在后面添加元素
    addBack(ele) {
      store.get(this)._items.push(ele);
      return this.size;
    }
    // 在后面移除元素
    removeBack() {
      return store.get(this)._items.pop();
    }
    // 返回第1个元素
    peekFront() {
      return store.get(this)._items[0];
    }
    // 返回最后一个元素
    peekBack() {
      return store.get(this)._items[this.size - 1];
    }
    // 默认迭代器 展示所有元素
    [Symbol.iterator]() {
      let i = 0;
      return {
        next: () => ({ value: store.get(this)._items[i], done: i++ === this.size }),
        [Symbol.iterator]() {
          return this;
        },
      };
    }
    // 清空所有元素
    clear() {
      store.get(this)._items = [];
    }
  }
  return Dequeue;
})();

2.2. 使用

const dequeue = new Dequeue();
dequeue.size = 2;
console.log(dequeue.size);
// -> 0
console.log(dequeue.addBack('orange'));
// -> 1
console.log(dequeue.addBack('pear'));
// -> 2
console.log(dequeue.addFront('apple'));
// -> 3
console.log(...dequeue);
// -> 'apple' 'orange' 'pear'
console.log(dequeue.peekFront());
// -> 'apple'
console.log(dequeue.peekBack());
// -> 'pear'
console.log(dequeue.removeBack());
// -> 'pear'
console.log(dequeue.removeFront());
// -> 'apple'
dequeue.clear();
console.log(dequeue.size);
// -> 0
console.log(dequeue);
// -> Dequeue {}

3.优先队列

在一般情况下,从队列中删除的元素,一定是率先入队的元素。但是也有一些使用队列的

应用,在删除元素时不必遵守先进先出的约定。这种应用,需要使用一个叫做优先队列的

数据结构来进行模拟。

从优先队列中删除元素时,需要考虑优先权的限制。比如医院急诊科(Emergency Department)的候诊室,就是一个采取优先队列的例子。当病人进入候诊室时,分诊护士会评估患者病情的严重程度,然后给一个优先级代码。高优先级的患者先于低优先级的患者就医,同样优先级的患者按照先来先服务的顺序就医。

3.1实现

const PriorityQueue = (() => {
    const store = new WeakMap();
    // 优先队列内部的元素类
    class QueueElement {
        constructor(element, priority) {
            this.element = element;
            this.priority = priority;
        }
    }
    class PriorityQueue {
        constructor() {
            store.set(this, { _items: [] });
        }
        // 只读 队列大小
        get size() {
            return store.get(this)._items.length;
        }
        // 只读 是否为空
        get isEmpty() {
            return this.size === 0;
        }
        // enqueue(element, priority) 入队,将元素按优先级加入到队列中
        // 重写 enqueue()
        enqueue(element, priority) {
            // 根据传入的元素,创建 QueueElement 对象
            const queueElement = new QueueElement(element, priority);
            // 判断队列是否为空
            if (this.isEmpty) {
                // 如果为空,不用判断优先级,直接添加
                store.get(this)._items.push(queueElement);
            } else {
                // 定义一个变量记录是否成功添加了新元素
                let added = false;
                for (let i = 0; i < this.size; i++) {
                    // 让新插入的元素进行优先级比较,priority 值越小,优先级越大
                    if (queueElement.priority < store.get(this)._items[i].priority) {
                        // 在指定的位置插入元素
                        store.get(this)._items.splice(i, 0, queueElement);
                        added = true;
                        break;
                    }
                }
                // 如果遍历完所有元素,优先级都大于新插入的元素,就将新插入的元素插入到最后
                if (!added) {
                    store.get(this)._items.push(queueElement);
                }
            }
            return this.size;
        }
        // 退出队列
        dequeue() {
            return store.get(this)._items.shift();
        }
        // 返回队列第 1 个元素
        peek() {
            return store.get(this)._items[0];
        }
        // 默认迭代器 用于展示所有元素
        [Symbol.iterator]() {
            let i = 0;
            return {
                next: () => ({ value: store.get(this)._items[i], done: i++ === this.size }),
                [Symbol.iterator]() {
                    return this;
                },
            };
        }
        // 清空所有元素
        clear() {
            store.get(this)._items = [];
        }
        // toString() 将队列中元素以字符串形式返回
        // 重写 toString()
        toString() {
            let result = "";
            for (let item of store.get(this)._items) {
                result += item.element + "-" + item.priority + " ";
            }
            return result;
        }
    }
    return PriorityQueue
})()

3.2使用

const queue = new PriorityQueue();
queue.size = 2;
console.log(queue.size);
// -> 0
console.log(queue);
// -> PriorityQueue {}
console.log(queue.enqueue('apple', 2));
// -> 1
console.log(queue.enqueue('orange', 1));
// -> 2
console.log(queue.enqueue('pear', 4));
// -> 3
console.log(...queue);
// -> 'apple' 'orange' 'pear'
console.log(queue.dequeue());
// -> 'apple'
console.log(queue.peek());
// -> 'orange'
console.log(queue.size);
// -> 2
queue.clear();
console.log(queue.isEmpty);
// -> true
console.log(queue.peek());
// -> undefined


目录
相关文章
|
1月前
数据结构——栈和队列
数据结构——栈和队列
16 1
|
20天前
|
算法 C语言
【数据结构与算法 经典例题】使用栈实现队列(图文详解)
【数据结构与算法 经典例题】使用栈实现队列(图文详解)
|
20天前
|
存储 算法
【数据结构和算法】--队列的特殊结构-循环队列
【数据结构和算法】--队列的特殊结构-循环队列
12 0
|
25天前
|
Python
数据结构===队列
数据结构===队列
|
20天前
|
存储 测试技术
【数据结构】操作受限的线性表,队列的具体实现
【数据结构】操作受限的线性表,队列的具体实现
21 4
|
20天前
|
算法 C语言
【数据结构与算法 经典例题】使用队列实现栈(图文详解)
【数据结构与算法 经典例题】使用队列实现栈(图文详解)
|
21天前
|
算法
【C/数据结构和算法】:栈和队列
【C/数据结构和算法】:栈和队列
23 1
|
27天前
|
算法
$停车场管理系统 栈与队列
$停车场管理系统 栈与队列
13 1
|
1月前
|
存储 算法 程序员
【C++进阶】深入STL之 栈与队列:数据结构探索之旅
【C++进阶】深入STL之 栈与队列:数据结构探索之旅
23 4
|
1月前
数据结构 栈 / 队列(第9天)
数据结构 栈 / 队列(第9天)