一、介绍
- 在 LIFO 数据结构中,将
首先处理添加到队列
中的最新元素
。 - 插入操作,在栈中被称作入栈
push
,总是在堆栈的
末尾添加一个新元素
- 删除操作,退栈
pop
,将始终在末尾删除一个元素
。
二、栈实现
栈的实现比队列容易。动态数组
足以实现堆栈结构。Leetcode提供的参考如下:
#include <iostream>
class MyStack {
private:
vector<int> data; // store elements
public:
/** Insert an element into the stack. */
void push(int x) {
data.push_back(x);
}
/** Checks whether the queue is empty or not. */
bool isEmpty() {
return data.empty();
}
/** Get the top item from the queue. */
int top() {
return data.back();
}
/** Delete an element from the queue. Return true if the operation is successful. */
bool pop() {
if (isEmpty()) {
return false;
}
data.pop_back();
return true;
}
};
int main() {
MyStack s;
s.push(1);
s.push(2);
s.push(3);
for (int i = 0; i < 4; ++i) {
if (!s.isEmpty()) {
cout << s.top() << endl;
}
cout << (s.pop() ? "true" : "false") << endl;
}
}
三、深度优先搜索DFS
深度优先搜索
(DFS)也可用于查找从根结点到目标结点的路径。
1. 结点的处理顺序
从根结点 A
开始。
- 首先,我们选择结点
B
的路径,并进行回溯,直到我们到达结点E
,我们无法更进一步深入。 - 然后我们回溯到
A
并选择第二条路径到结点C
。从C
开始,我们尝试第一条路径到E
但是E
已被访问过。所以我们回到C
并尝试从另一条路径到F
。最后,我们找到了G
。
总的来说,在我们到达最深的
结点之后,我们只
会回溯并尝试另一条路径。
因此,在 DFS 中找到的第一条路径并不总是最短的路径。例如,在上面的例子中,我们成功找出了路径
A-> C-> F-> G
并停止了 DFS。但这不是从A
到G
的最短路径。
2. 栈的入栈和退栈顺序
- 首先将根结点推入到栈中;
- 然后我们尝试第一个邻居
B
并将结点B
推入到栈中; - 等等等等。
- 当我们到达最深的结点
E
时,我们需要回溯。 - 当我们回溯时,我们将从栈中
弹出最深的结点
,这实际上是推入到栈中的最后一个结点
。
结点的处理顺序是完全相反的顺序
,就像它们被添加
到栈中一样,它是后进先出(LIFO)。这就是我们在 DFS 中使用栈的原因。
四、两种方法
大多数情况下,我们在能使用 BFS 时也可以使用 DFS。但是有一个重要的区别:遍历顺序
。
与 BFS 不同,更早访问的结点可能不是更靠近根结点的结点
。因此,你在 DFS 中找到的第一条路径可能不是最短路径
。
两种实现 DFS 的方法。
第一种方法是递归,模板如下:
boolean DFS(Node cur, Node target, Set<Node> visited) {
return true if cur is target;
for (next : each neighbor of cur) {
if (next is not in visited) {
add next to visted;
return true if DFS(next, target, visited) == true;
}
}
return false;
}
这种方法,我们使用的是由系统提供的隐式栈,也称为调用栈(Call Stack)。
每个元素都需要固定的空间。栈的大小正好是 DFS 的深度。因此,在最坏的情况下,维护系统栈需要 O(h),其中 h 是 DFS 的最大深度。在计算空间复杂度时,永远不要忘记考虑系统栈。
第二种是使用显式栈的DFS
递归解决方案的优点是它更容易实现。 但是,存在一个很大的缺点:如果递归的深度太高,你将遭受堆栈溢出。
在这种情况下,可以使用 BFS,或使用显式栈实现 DFS。
该逻辑与递归解决方案完全相同。 但我们使用 while
循环和栈
来模拟递归期间的系统调用栈
。
boolean DFS(int root, int target) {
Set<Node> visited;
Stack<Node> s;
add root to s;
while (s is not empty) {
Node cur = the top element in s;
return true if cur is target;
for (Node next : the neighbors of cur) {
if (next is not in visited) {
add next to s;
add next to visited;
}
}
remove cur from s;
}
return false;
}
五、相关的Leetcode例子
Leetcode算法题:岛屿数量(BFS&DFS)