我对上面文本片段的问题是:
- 为什么层序遍历不使用递归?
- 队列在层序遍历中如何使用?请用伪代码解释。
我认为从第二个问题开始会更容易。一旦你理解了第二个问题的答案,你就能更好地理解第一个问题的答案。
层序遍历的工作原理
我认为理解层序遍历的工作原理最好的方法是逐步执行,所以让我们这样做。
我们有一棵树。
![enter image description here](https://istack.dev59.com/lITIn.webp)
我们想要按层级遍历它。
![enter image description here](https://istack.dev59.com/QaeFT.webp)
因此,我们访问节点的顺序将是A B C D E F G。为此,我们使用队列。请记住,队列是先进先出(FIFO)。我喜欢想象节点正在排队等待服务员处理。
![enter image description here](https://istack.dev59.com/cETTc.webp)
让我们先将第一个节点A
放入队列中。
![enter image description here](https://istack.dev59.com/AZbtc.webp)
好的,系好安全带。设置已经完成,我们即将开始深入探讨。
第一步是将 A
从队列中取出以便进行处理。但等等!在这样做之前,让我们也将 A
的 子节点,B
和 C
,也放入队列中。
![enter image description here](https://istack.dev59.com/3dQ9L.webp)
注意:
A
实际上在这个时候已经不在队列中了。我将其变灰以尝试传达这一点。如果我完全从图表中删除它,那么后面的故事中发生的事情就会更难以想象。
注意:在图表中,
A
正在由服务台的服务员处理。在现实生活中,处理节点可以意味着很多事情。例如使用它来计算总和、发送短信、记录到控制台等等。根据我图表中的比喻,您可以告诉服务员您希望他们如何处理该节点。
现在我们转向下一个节点,即
B
。
我们做与
A
相同的事情:1)将子节点添加到队列中,2)处理该节点。
![enter image description here](https://istack.dev59.com/eN8ez.webp)
嘿,看看!似乎我们正在做的事情将为我们带来我们所寻求的层序遍历!让我们继续跟随步骤来证明这一点。
一旦我们完成了B,接下来就是C。我们将C的子节点放在队列的末尾,然后处理C。
![enter image description here](https://istack.dev59.com/Ilaq6.webp)
现在让我们看看接下来会发生什么。下一个是
D
。
D
没有子节点,因此我们不会将任何内容放在队列的末尾。我们只处理
D
。
![enter image description here](https://istack.dev59.com/Pvh51.webp)
然后对于E
、F
和G
也是同样的处理。
为什么不使用递归
想象一下,如果我们使用栈而不是队列会发生什么。 让我们倒回到我们刚刚访问A
的那个点。
![enter image description here](https://istack.dev59.com/V6oNH.webp)
这是使用栈的情况下的显示效果。
![enter image description here](https://istack.dev59.com/oSBU9.webp)
现在,这位新的服务员不再按照顺序服务,他喜欢先为最近的客户提供服务,而不是等待时间最长的客户。因此,下一个要服务的是
C
,而不是
B
。
这里是关键点。堆栈开始引起不同的处理顺序,与队列有所不同。
与以前一样,我们添加
C
的子节点,然后处理
C
。这次我们只是将它们添加到堆栈中,而不是队列中。
![enter image description here](https://istack.dev59.com/m8BjB.webp)
现在,接下来呢?这位新服务员喜欢先为最近的客户提供服务(即,我们使用一个栈),所以
G
是下一个。
我会在这里停止执行。重点是,仅仅将队列替换为栈这样简单的操作实际上给我们带来了完全不同的执行顺序。我鼓励你继续完成这个步骤。
你可能会想:“好吧...但问题问到了递归。这与递归有什么关系?”那么,当你使用递归时,有些诡异的事情正在发生。你从未使用过像
s = new Stack()
这样的栈数据结构。然而,运行时使用了调用栈。这最终在概念上类似于我上面做的事情,因此不能给我们从层次遍历中得到
A B C D E F G
的顺序。