队列
队列
定义
队列,是一种「先进先出」的线性数据结构。
理解
一般情况下,元素从一端进入队列,从另一端离开队列。元素进入的一端称为队尾,元素离开的一端称为队首。
添加元素时,只能将其插入到队尾(即入队操作);删除元素时,只能将队首元素从队列中取出(即出队操作)。
代码模板
涉及到队列的操作,可以方便地利用标准模板库 STL 中的 queue 实现。
1 | |
具体应用
约瑟夫环问题
n 个人围成一个圈玩游戏,按照顺时针顺序从 1 到 n 编号。也就是说,从第 i 个人顺时针数一位是第 i + 1 个人,从第 n 个人顺时针数一位是第 1 个人。
游戏规则如下:
- 从第 1 个人的位置开始。每次沿着顺时针方向数 k 个人,数的时候要包含开始数的那个人。数到的第 k 个人需要离开圈子,意味着输掉游戏。
- 如果圈子里剩余的人不止一个,那么从刚刚输掉游戏的人的下一位开始继续数 k 个,并进行淘汰。
- 如果圈子里只剩一个人,那么他获得最终的胜利。
最直观的方法是模拟游戏过程。使用队列存储圈子中人的编号,初始时将 1 到 n 的所有编号依次加入队列,队首元素即为第 1 个人的编号。
每一轮游戏中,从当前编号开始数 k 个,数到的第 k 个人离开圈子。模拟这个过程的做法是,将队首元素取出并将该元素在队尾处重新加入队列(模拟绕圈),重复该操作 k − 1 次,则在 k − 1 次操作之后,队首元素即为这一轮中数到的第 k 个人的编号,将队首元素取出并且不放回队尾,即为数到的第 k 个人离开圈子。上述操作之后,新的队首元素即为下一轮游戏开始数的起始编号。每一轮游戏之后,圈子中减少一个人,队列中减少一个元素。
重复上述过程,直到队列中只剩下 1 个元素,该元素即为获胜者的编号。
1 | |
集合的前 N 个元素
有这样一个集合 M,定义如下:
- 数 1 属于 M。
- 如果 X 属于 M,则 Y = 2 * X + 1和 Z = 3 * X + 1 也属于 M。
- 此外再没有别的数属于 M。
按递增次序生成集合 M 中最小的 N 个数,输出第 N 小的数。
可以用两个队列 a 和 b 分别来存放 Y 和 Z 中的数,然后递推求出最小的第 N 项,方法如下:
- 初始时 M 中只有 1,队列 a 和 b 为空。
- 从 M 中取出最大的数 x,将 2 * x + 1 和 3 * x + 1 分别放入队列 a 和队列 b 的队尾。
- 将队列 a 和队列 b 的队首元素进行比较,可能有三种情况:
a. a.front() > b.front()
b. a.front() = b.front()
c. a.front() < b.front()
将比较的小者取出送入 M(会成为 M 中新的最大值),取出数的那个队列删除队首元素。- 重复步骤 2 和 3 直至取出第 N 项为止。
1 | |
双端队列
定义
双端队列,是一种线性数据结构,可以在队首和队尾进行元素的插入和删除。
理解
双端队列可以视为栈和队列功能的结合。一般情况下,双端队列支持四个操作:
- 在队首插入一个元素
- 在队首删除一个元素
- 在队尾插入一个元素
- 在队尾删除一个元素
代码模板
涉及到双端队列的操作,可以方便地利用标准模板库 STL 中的 deque 实现。
1 | |