基础知识
队列是连续的存储区,可以存储一系列的元素。是FIFO(先入先出,First-In-First-Out)结构。
队列通常具有头尾指针(左闭右开区间),头指针指向第一个元素,尾指针指向最后一个元素的下一位。
队列支持(从队尾)入队(enqueue)、(从队首)出队(dequeue)操作.
循环队列可以通过取模操作更充分的利用空间。
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队首。
类似于我们日常生活中排队买票,我们购票的时候是要排在队尾的——入队操作,当到我们
买票的时候我们已经在队首了,买完票离开就是一个出队的操作。总结来说,队列就是一个
先进先出的线性表。
队列的典型应用场景
1、CPU的超线程技术
2、线程池的任务队列
经典面试题
LeetCode #86 分隔链表
使用两个链表,一个用于插入小于x的元素,一个用于插入大于等于x的元素,最后合并两个链表即可。
LeetCode #138 复制带随机指针的链表
难点在于复制随机指针。
这里可以使用一个小技巧对结点进行复制:
将原本A->B->C复制成A->A’->B->B’->C->C’.
然后将复制节点中的随机指针域向后推进一格,这样复制节点的随机指针域,就指向了随机指针的复制节点。
然后将复制的节点拆下来即可。
LeetCode #622设计循环队列
LeetCode #641设计双端循环队列
LeetCode #1670设计前中后队列
为便于后续操作,首先实现一个基于双向链表的双端队列。使用两个双端队列,一个存放前半部分元素,一个存放后半部分元素。通过维护两个队列首尾节点的方式,平衡两个队列的元素数量,使得中间的元素始终处于固定的位置(如维持在第二个链表头部、或第一个链表尾部)。
LeetCode #933 最近请求次数
使用队列对过程进行模拟。不断弹出队首的过期元素,然后返回队列大小即可。由于只需要最近的3000以内的数据,每次加入后,然后判断序列最先边的数据是否在这个范围内,如何不在就把它移出,最重要的是,这个输入的时间序列是一个有序的,程序的时间复杂度为:一个平均的时间节点插入数据的个数
LeetCode #面试题 17.09 第K个数
LeetCode #859 亲密字符串
LeetCode #860 柠檬水找零
LeetCode #969 煎饼排序
每次将第N大的元素先翻转到第1位,再翻转到第N位,这样第N位就无需在后续进程中再进行处理,只需要考虑前N-1位即可。由于每个元素只需要2次翻转即可归位,因此所需的次数最多只需2N次,符合题目需求。对于这种做法,可行的优化主要有两个。
- 一是可以去除值为“1”的翻转(值为“1”的翻转相当于未操作);
- 二是可以跳过已经在正确位置上的元素。
对于煎饼排序的最优解,在《编程之美》中有更加详细的讨论,感兴趣的同学可以自行阅读。
LeetCode #621 任务调度
首先考虑这样一个问题,对于存在冷却时间的情况,任务调度最少要花费多少的空间?
我们首先对任务进行分类,根据任务出现的次数对任务进行排序。
以{“A”: 10, “B”: 8, “C”: 5, “D”: 3, “E”: 1},且冷却时间为2为例。
无论如何安排,任务A都至少要花费9 * (1 + n) + 1的时间,原因如图所示(带有黑色边框的部分是无法节省的时间部分,蓝色部分为冷却时间)。