队列:先入先出的数据结构
在FIFO数据结构中,将首先处理添加到队列中的第一个元素。
队列的实现
为了实现队列,我们可以使用动态数组和指向队列头部的索引。队列应支持两种操作:入队和出队。入队向队列追加一个元素,而出队会删除第一个元素。
1 | class MyQueue{ |
上述实现的缺点:随着起始指针的移动,浪费了越来越多的空间,当我们有空间限制时,这将是难以接受的。
循环队列
为了解决上述问题,提供了一种简单但低效的队列实现。具体来说,我们可以使用固定大小的数组和两个指针来指示起始位置和结束位置。目的是重用被浪费的存储。
1 | class MyCircularQueue { |
数据流中的移动平均值
1 | class MovingAverage { |
队列和BFS
广度优先搜索(BFS)的一个常见应用是找出根节点到目标节点的最短路径。
结点的处理顺序是:在第一轮中,处理根结点。第二轮中,处理根结点旁边的结点;第三轮中,处理距离根结点两步的结点,等等。
与树的层序遍历类似,越是接近根结点的结点将越早地遍历
在第K轮种将结点X添加到队列中,则根结点与X之间的最短路径长度恰好是K。也就是说,第一次找到目标节点时,你已经处于最短路径中。
队列的入队和出队顺序是什么:首先将根结点排入队列,然后在每一轮中,我们逐个处理已经排在队列中的结点,并将所有的邻居添加到队列中。新添加的结点不会立即遍历,而是在下一轮中处理。
广度优先搜索-模板
BFS的两个主要方案:遍历和找出最短路径。通常,这发生在树或图中。
模板一
1 | int BFS(Node root, Node target){ |
模板二
有时,确保我们永远不会访问一个结点两次,否则,可能陷入无限循环。我们可以在上面的代码中添加一个哈希集来解决这问题
1 | int BFS(Node root, Node target){ |
墙与门
1 | class Solution { |
岛屿数量
1 | class Solution { |
打开转盘锁
1 | class Solution { |
完全平方数
1 | class Solution { |
后入先出的数据结构
在LIFO数据结构中,将首先处理添加到队列中的最新元素。与队列不同,栈是一个LIFO数据结构。通常,插入操作在栈中被称作入栈push。与队列类似,总是在堆栈的末尾添加一个新元素。但是,删除操作,退栈pop,将始终删除队列中相对于它的最后一个元素。
栈的实现
1 | class MyStack{ |
最小栈
1 | class MinStack { |
有效括号
1 | class Solution { |
每日温度
1 | class Solution { |
逆波兰表达式求值
1 | class Solution { |
栈和深度优先搜索
与BFS类似,深度优先搜索是用于在树/图中遍历/搜索的另外一种重要算法。也可以在更抽象的场景中使用。
正如树的遍历中所提到的,我们可以用DFS进行前序遍历,中序遍历,后序遍历。在这三个遍历顺序中有一个共同的特性:除非我们到达最深的结点,否则我们永远不会回溯。
DFS和BFS最大的区别,BFS永远不会深入搜索,除非它已经在当前层级访问了所有结点。
通常,我们使用递归实现DFS。栈在递归中起着重要的作用。
DFS-模板
在大多数情况下,我们在能使用BFS时也可以使用DFS。但是有一个重要的区别:遍历顺序。
与BFS不同,更早访问的结点可能不是更靠近根结点的结点。因此,在DFS中找到的第一条路径可能不是最短路径。
模板-递归
1 | boolean DFS(Node cur, Node target, Set<Node> visited){ |
岛屿数量
1 | class Solution { |
克隆图
1 | /* |
目标和
1 | class Solution { |
DFS-模板II
1 | boolean DFS(int root, int target){ |
二叉树的中序遍历
1 | /** |
用栈实现队列
1 | class MyQueue { |
用队列实现栈
1 | class MyStack { |
字符串解码
1 | class Solution { |
图像渲染
1 | class Solution { |
01矩阵
1 | class Solution { |