手写数据结构 队列篇

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

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


目录
相关文章
|
14天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
90 9
|
1月前
|
缓存 算法 调度
数据结构之 - 双端队列数据结构详解: 从基础到实现
数据结构之 - 双端队列数据结构详解: 从基础到实现
61 5
|
1月前
|
存储 算法 搜索推荐
探索常见数据结构:数组、链表、栈、队列、树和图
探索常见数据结构:数组、链表、栈、队列、树和图
99 64
|
17天前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
1月前
初步认识栈和队列
初步认识栈和队列
58 10
|
1月前
|
存储 算法 定位技术
数据结构与算法学习二、稀疏数组与队列,数组模拟队列,模拟环形队列
这篇文章主要介绍了稀疏数组和队列的概念、应用实例以及如何使用数组模拟队列和环形队列的实现方法。
20 0
数据结构与算法学习二、稀疏数组与队列,数组模拟队列,模拟环形队列
|
1月前
|
存储 安全 Java
【用Java学习数据结构系列】探索栈和队列的无尽秘密
【用Java学习数据结构系列】探索栈和队列的无尽秘密
30 2
【数据结构】--- 栈和队列
【数据结构】--- 栈和队列
|
1月前
|
消息中间件 存储 Java
数据结构之 - 深入探析队列数据结构: 助你理解其原理与应用
数据结构之 - 深入探析队列数据结构: 助你理解其原理与应用
29 4
|
1月前
【初阶数据结构】深入解析队列:探索底层逻辑
【初阶数据结构】深入解析队列:探索底层逻辑