二叉树的各种操作(递归和非递归遍历,树深度,结点个数等等)
二叉树建立
先给出结点结构:
1 | static class Node { |
两种建立方式:
- 可以根据二叉树根节点和左右子结点的下标关系递归建立二叉树,层次输入二叉树结点;
- 也可以使用输入流前序建立二叉树(注意空树要输入-1);
1、根据下标关系
1 | // given a arr to build |
大致过程如下:
2、前序输入(cin)建立
1 | // cin method |
过程如下:
前序遍历
1、递归前序
1 | static void preOrder(Node T) { |
2、非递归前序
前序遍历顺序为: 根结点->左子树->右子树,所以对于正在访问的根结点,可以直接访问,访问完之后,按照相同的方式访问左子树,再访问右子树,过程如下 :
- 如果当前节点
p不为空,访问结点p,并将结点p入栈,并继续访问左子树(直到左子树为空); - 否则将栈顶元素出栈,并访问栈顶的元素的右子树;
- 直到栈为空且
p为空,循环结束。
代码:
1 | static void iterativePre(Node root) { |
也可以将上面的一直访问到左子树为空写成一个while循环:
1 | static void iterativePre2(Node root) { |
还有另外一种写法是:
- 先把根节点入栈,然后每次出栈一个元素,先访问这个元素,然后如果它的右子树存在,就入栈,如果它的左子树存在也入栈;
- 为什么要先入右子树呢,因为,前序遍历是中->左->右,而栈可以逆序,所以先右再左;
这个方法在后续遍历的双栈法中有体现,那个只是这个稍微的修改。
1 | static void iterativePre3(Node root) { |
中序遍历
1、递归中序
1 | static void inOrder(Node T) { |
2、非递归中序
中序遍历 : 左子树->根->右子树,过程如下:
- 当前节点不空
!= null,压入栈中(和前序遍历不同的是,不需要打印),当前节点向左; - 当前节点为空
== null,从栈中拿出一个并且打印(在这里打印) ,当前节点向右;
直到栈为空且p为空,循环结束。
1 | /** |
同理,那个一直访问左孩子那里也可以改成whlie:
1 | static void iterativeIn2(Node root) { |
后序遍历
1、递归后序
1 | static void postOrder(Node T) { |
2、非递归后序
1)、双栈法
这个其实就是非递归前序(iterativePre3)的稍微一点改进。
- 首先,前序遍历入栈(
iterativePre3)的顺序是先 右 再左; - 这时,我们可以做到反过来先 左 再右,这样遍历的顺序可以做到 **”中右左”**,而后续遍历是 “左右中”,正好是前面那个的相反,所以我们再使用一个栈反转保存即可;
代码:
1 | /** |
2)、设置pre结点
过程如下:
- 对于任一结点
p,先将其入栈; - 可以访问的情况: ①若
p不存在左孩子和右孩子,则可以直接访问它。②或者p存在左孩子或者右孩子,但是左孩子和右孩子都已经被访问过了,则也可以直接访问该结点; - 若非上述两种情况,则将右孩子和左孩子依次入栈。这样可以保证每次取栈顶元素时,左孩子在右孩子前面被访问,根结点在左孩子和右孩子访问之后被访问;
代码:
1 | /*** 非递归后续2(设置pre结点) */ |
层次遍历
很简单。利用队列BFS即可,每次访问完p,若左右孩子存在,则入队,直至队空;
1 | static void levelOrder(Node root) { |
寻找树中有没有值为x的结点
递归条件有两个,一个是为空代表没找到,找到了的话直接返回,否则递归查找左右子树。
1 | //查找某个值为x的结点 |
统计树中结点的个数
树中结点的个数等于根节点(1) + 左子树结点个数 + 右子树的个数,递归求解即可。
1 | //统计结点个数 |
计算树的高度
也是递归求解,左右子树的高度中的比较高的加上根节点就是树的高度。
1 | //计算二叉树的深度 |
判断两棵树是不是相等
也是递归求解,两棵树相等,既要根节点的值相等,而且左右子树也要相等。
1 | //判断两棵树是不是相等 |
完整测试代码
完整的测试代码,这里输入的样例树(就是建树的时候那个例子)如下:

代码:
1 | import java.io.BufferedInputStream; |
运行效果如下图:
