背景描述
二叉树遍历相信大家在学习数据结构的时候都学习过,有递归方法和非递归方法,递归方法简单,容易理解,不在本次的讨论范围内。因此本篇文章主要是讨论非递归的方法,也就是迭代法。这种方法网上有很多解题方法,先序,后序,中序还都不一样,很难理解。即便当时理解了,过段时间再让你写,你也是懵逼的。下面我将介绍一种方法,这种方法可以说利器,只要掌握了它的思想,你会发现先序,中序,后序,只是调整一行代码的顺序,就都能解决出来,可谓是一招鲜,吃遍天。
方法描述
如图是一棵二叉树,我们以先序遍历分析,我们访问二叉树中的节点可以看作三种操作,打印该节点,访问该节点的左孩子,访问该节点的右孩子。举例说明,假如我们要访问节点 A,那么会分解为如下的操作 :
- print A
- visit B
visit C
首先我们会打印A,然后访问节点B,那么访问节点B又会被分解位三个操作print B
- visit D
- visit E
上一张图大家更容易理解,很明显这需要一个数据结构队列或者栈来实现
代码实现
这里编写代码的语言是Java,其他语言可以参照此思路进行编写代码
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
//存放结果
List<Integer> result = new ArrayList<>();
//声明一个队列
Deque<Guide> path = new ArrayDeque<>();
//将根节点放到队首
path.addFirst(new Guide(0,root));
while(!path.isEmpty()){
//访问队列中的队首元素
Guide current = path.removeFirst();
//这里需要做判断,因为空节点也被放进了队列
if(current.treeNode==null){
continue;
}
//op= 1,说明是print该节点,遂将其放进结果容器中
if(current.op==1){
result.add(current.treeNode.val);
}else{
//访问该节点,于是将该节点的三个操作放到队列中 print 自己,visist 左孩子,visit右孩子
path.addFirst(new Guide(0,current.treeNode.right));
path.addFirst(new Guide(0,current.treeNode.left));
path.addFirst(new Guide(1,current.treeNode));
}
}
return result;
}
private class Guide{
//0 代表visit该节点,1 print 代表打印该节点
int op;
TreeNode treeNode;
public Guide(int op,TreeNode node){
this.op = op;
this.treeNode = node;
}
}
}
这里比较巧妙的是我们将节点和节点的操作封装成一个对象存放到队列中,对于一个节点要么是被打印,要么是被访问。
注:这里是用队列来实现的,很明显用栈也可以,只是和队列的放入顺序是相反的。(removeFirst相当于队首元素出队,addFirst相当于往队列的队头加元素)
按照这种思路,中序遍历和后序遍历就很容易了,只需要动一行代码就可以搞定
中序遍历:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
Deque<Guide> path = new ArrayDeque<>();
path.addFirst(new Guide(0,root));
while(!path.isEmpty()){
//取出队首元素
Guide current = path.removeFirst();
if(current.node==null) continue;
if(current.op==1){
result.add(current.node.val);
}else{
path.addFirst(new Guide(0,current.node.right));
path.addFirst(new Guide(1,current.node));
path.addFirst(new Guide(0,current.node.left));
}
}
return result;
}
private class Guide{
int op;
TreeNode node;
public Guide(int op, TreeNode node){
this.op = op; //0 visit,1 print
this.node = node;
}
}
}
后序遍历
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
Deque<Guide> path = new ArrayDeque<>();
path.addFirst(new Guide(0,root));
while(!path.isEmpty()){
//取出队首元素
Guide current = path.removeFirst();
if(current.node==null) continue;
if(current.op==1){
result.add(current.node.val);
}else{
path.addFirst(new Guide(1,current.node));
path.addFirst(new Guide(0,current.node.right));
path.addFirst(new Guide(0,current.node.left));
}
}
return result;
}
private class Guide{
int op;
TreeNode node;
public Guide(int op, TreeNode node){
this.op = op; //0 visit,1 print
this.node = node;
}
}
}
总结
通过以上分析和代码的实现,我们发现,先序,中序,后序只需要根据其访问的规则调整这三行代码的顺序就可以了,其他代码一行都不用动。
参考:
youtube视频