目录

  • 建立二叉树
  • 递归前序和非递归前序
  • 递归中序和非递归中序
  • 递归后续和非递归后续(包括双栈法和设置pre结点)
  • 层次遍历
  • 寻找树中有没有值为x的结点
  • 统计树中结点的个数
  • 计算树的高度
  • 判断两颗树是不是相等
  • 前序中序,中序后续建立二叉树
  • 树的子结构
  • 二叉树镜像
  • 完整测试代码

二叉树建立

先给出结点结构:

    private static class Node{public int val;public Node left;public Node right;public Node(int val) {this.val = val;}}
  • 可以根据二叉树根节点和左右子结点的下标关系递归建立二叉树,层次输入二叉树结点;
  • 也可以使用输入流前序建立二叉树(注意空树要输入-1);
    // given a arr to buildpublic static Node createTree(int arr[],int i) {if(i >= arr.length || arr[i] == -1) return null;Node T = new Node(arr[i]);T.left = createTree(arr,2*i + 1);T.right = createTree(arr,2*i + 2);return T;}


前序输入建立:

    // cin method    public static Node buildTree(Scanner cin) {Node root = null;int data = cin.nextInt();if (data != -1) {root = new Node(data);root.left = buildTree(cin);root.right = buildTree(cin);}return root;}

前序遍历

递归前序:
    public static void preOrder(Node T) {if(null != T) {System.out.print(T.val + " ");preOrder(T.left);preOrder(T.right);} }
非递归前序遍历:

前序遍历可以归纳为: 根结点->左子树->右子树,所以对于正在访问的根结点,可以直接访问,访问完之后,按照相同的方式访问左子树,再访问右子树,过程如下 :

  • 访问结点p,并将结点p入栈。
  • 判断结点p的左孩子是否为空,若不为空,则继续访问左孩子,否则将栈顶元素出栈,并访问栈顶的元素的右孩子。
  • 直到栈为空且p为空,循环结束。
    public static void iterativePre(Node T) {Stack<Node>s = new Stack<Node>();Node p = T;while(!s.empty() || p != null) {if(p != null) {s.push(p);System.out.print(p.val + " ");p = p.left;}else {p = s.pop();p = p.right;}}}

或者:(这里使用一个List存储遍历结果)
原题链接

  public List<Integer> preorderTraversal(TreeNode root) {List<Integer>res = new ArrayList<>();if(res == null)return res;Stack<TreeNode>stack = new Stack<>();TreeNode cur = root;while(!stack.isEmpty() || cur != null){while(cur != null){stack.push(cur);res.add(cur.val); //先根cur = cur.left;}    cur = stack.pop();cur = cur.right;}return res;}

还有另外一种更加容易理解的写法是:

  • 先把根节点入栈,然后每次出栈一个元素,先访问这个元素,然后如果它的右子树存在,就入栈,如果它的左子树存在也入栈;
  • 为什么要先入右子树呢,因为,前序遍历是中->左->右,而栈可以逆序,所以先右再左;
  • 这个方法在后续遍历的双栈法中有体现,那个只是这个稍微的修改;
    /**  理解 :  push右子树,再push左子树,这样的话弹栈的时候就是先访问左子树,再右子树*/public static void iterativePre_2(Node T){ if(T == null)return;Stack<Node>stack = new Stack<>();stack.add(T);while(!stack.isEmpty()){T = stack.pop();System.out.print(T.val + " ");if(T.right != null)stack.push(T.right);if(T.left != null)stack.push(T.left);}System.out.println();}

中序遍历

递归中序:
    public static void inOrder(Node T) {if(null != T) {inOrder(T.left);System.out.print(T.val + " ");inOrder(T.right);}}
非递归中序:

中序遍历 : 左子树->根->右子树,过程如下:

  • 当前节点为空,从栈中拿出一个并且打印 ,当前节点向右;
  • 当前节点不空,压入栈中,当前节点向左;

直到栈为空且p为空,循环结束。

    /*** 非递归中序* 理解: 就是两步*        1):  当前节点为空 ,从栈中拿出一个并且打印 ,当前节点向右*        2):  当前节点不空,  压入栈中, 当前节点向左  */ public static void iterativeIn(Node T) {if(T == null)return;Stack<Node>s = new Stack<Node>();Node p = T;while(!s.empty() || p != null) {if(p != null) {s.push(p);p = p.left;}else {p = s.pop();System.out.print(p.val + " ");p = p.right;}}}

后序遍历

递归后序:
    public static void postOrder(Node T) {if(null != T) {postOrder(T.left);postOrder(T.right);System.out.print(T.val + " ");}}
非递归后序:

①.双栈法:

  • 这个其实就是非递归前序的稍微一点改进,首先 前序遍历入栈的顺序是先 右 再左,这时,我们可以做到反过来先 左 再右,这样遍历的顺序可以做到 “中右左”,而后续遍历是 “左右中”,正好是前面那个的相反,所以我们再使用一个栈保存即可。

2.设置pre结点:

  • 对于任一结点p,先将其入栈;
  • 若p不存在左孩子和右孩子,则可以直接访问它;
  • 或者p存在左孩子或者右孩子,但是左孩子和右孩子都已经被访问过了,则可以直接访问该结点;
  • 若非上述两种情况,则将右孩子和左孩子依次入栈。这样可以保证每次取栈顶元素时,左孩子在右孩子前面被访问,根结点在左孩子和右孩子访问之后被访问;
    /*** 非递归后续1(双栈法解决非递归后续)* 后续遍历是要实现   左->右->中* 这个方法和前序遍历的第二种方法 只是多了一个栈而已  * 因为 前序遍历是  中->左->右  压栈顺序是 右->左*      这样,我们就很容易实现 中->右->左遍历  压栈顺序是 左->右*      而后续遍历是要实现  左->右->中,  *         我们把上面的  中右左 压入到另一个栈中 就实现了 左右中*/public static void iterativePos(Node T){ Stack<Node>s = new Stack<Node>(),s2 = new Stack<Node>();Node p = T;s.push(T);while(!s.empty()) {p = s.pop();s2.push(p);if(p.left != null)s.push(p.left);if(p.right != null)s.push(p.right);}while(!s2.empty())System.out.print(s2.pop().val + " ");}/** 非递归后续2(设置pre结点) */public static void iterativePos_2(Node T){Node cur,pre = null; Stack<Node>s = new Stack<Node>();s.push(T);while(!s.empty()) {cur = s.peek();if((cur.left == null && cur.right == null) || ((pre != null) && (pre == cur.left || pre == cur.right))) {System.out.print(cur.val + " ");s.pop();pre = cur;}else {if(cur.right != null)s.push(cur.right);if(cur.left != null)s.push(cur.left);}}}

层次遍历

利用队列BFS即可,每次访问完p,若左右孩子存在,则入队,直至队空;

    public static void levelOrder(Node T) {Queue<Node>q = new LinkedList<Node>();if(T != null) {q.add(T);while(!q.isEmpty()) {Node now = q.poll();System.out.print(now.val + " ");if(now.left != null)q.add(now.left);if(now.right != null)q.add(now.right);}}}

寻找树中有没有值为x的结点

递归条件有两个,一个是为空代表没找到,找到了的话直接返回,否则递归查找左右子树。

    //查找某个值为x的结点public static Node search(Node T,int x) {if(T == null) return null;if(T.val == x)return T;else {if(search(T.left,x) == null)  return search(T.right,x);else return search(T.left,x);}}

统计树中结点的个数

树中结点的个数等于根节点(1) + 左子树结点个数 + 右子树的个数,递归求解即可。

    //统计结点个数public static int count(Node T) {if(T != null) {return count(T.left) + count(T.right) + 1;}else return 0;}

计算树的高度

也是递归求解,左右子树的高度中的比较高的加上根节点就是树的高度

    //计算二叉树的深度public static int depth(Node T) {if(T == null)return 0;int l = depth(T.left);int r = depth(T.right);return l > r ? l + 1 : r + 1;}

判断两棵树是不是相等

也是递归求解,两棵树相等,既要根节点的值相等,而且左右子树也要相等。

    //判断两棵树是不是相等public static boolean is_SameTree(Node T1,Node T2) {if(T1 == null && T2 == null)return true;else {return T1 != null && T2 != null && T1.val == T2.val&& is_SameTree(T1.left,T2.left) && is_SameTree(T1.right,T2.right);}}

前序中序,中序后续建立二叉树

这个知识请看剑指offer刷题小结一的第四题。

树的子结构

这个知识请看剑指offer刷题小结三的第五题。

二叉树镜像

这个知识请看剑指offer刷题小结四的第六题。


完整测试代码

完整的测试代码如下 :

import java.io.BufferedInputStream;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
import java.util.Stack;public class BinaryTree {private static class Node{public int val;public Node left;public Node right;public Node(int val) {this.val = val;}}// given a arr to buildpublic static Node createTree(int arr[],int i) {if(i >= arr.length || arr[i] == -1) return null;Node T = new Node(arr[i]);T.left = createTree(arr,2*i + 1);T.right = createTree(arr,2*i + 2);return T;}// cin method  public static Node buildTree(Scanner cin) {Node root = null;int data = cin.nextInt();if (data != -1) {root = new Node(data);root.left = buildTree(cin);root.right = buildTree(cin);}return root;}public static void preOrder(Node T) {if(null != T) {System.out.print(T.val + " ");preOrder(T.left);preOrder(T.right);} }public static void iterativePre(Node T) {Stack<Node>s = new Stack<Node>();Node p = T;while(!s.empty() || p != null) {if(p != null) {s.push(p);System.out.print(p.val + " ");p = p.left;}else {p = s.pop();p = p.right;}}}/**   理解 :  push右子树,再push左子树,这样的话弹栈的时候就是先访问左子树,再右子树*/public static void iterativePre_2(Node T){ if(T == null)return;Stack<Node>stack = new Stack<>();stack.add(T);while(!stack.isEmpty()){T = stack.pop();System.out.print(T.val + " ");if(T.right != null)stack.push(T.right);if(T.left != null)stack.push(T.left);}System.out.println();}public static void inOrder(Node T) {if(null != T) {inOrder(T.left);System.out.print(T.val + " ");inOrder(T.right);}}/*** 非递归中序* 理解: 就是两步*        1):  当前节点为空 ,从栈中拿出一个并且打印 ,当前节点向右*        2):  当前节点不空,  压入栈中, 当前节点向左  */ public static void iterativeIn(Node T) {if(T == null)return;Stack<Node>s = new Stack<Node>();Node p = T;while(!s.empty() || p != null) {if(p != null) {s.push(p);p = p.left;}else {p = s.pop();System.out.print(p.val + " ");p = p.right;}}}public static void postOrder(Node T) {if(null != T) {postOrder(T.left);postOrder(T.right);System.out.print(T.val + " ");}}/*** 非递归后续1(双栈法解决非递归后续)* 后续遍历是要实现   左->右->中* 这个方法和前序遍历的第二种方法 只是多了一个栈而已  * 因为 前序遍历是  中->左->右  压栈顺序是 右->左*         这样,我们就很容易实现 中->右->左遍历  压栈顺序是 左->右*      而后续遍历是要实现  左->右->中,  *         我们把上面的  中右左 压入到另一个栈中 就实现了 左右中*/public static void iterativePos(Node T){ Stack<Node>s = new Stack<Node>(),s2 = new Stack<Node>();Node p = T;s.push(T);while(!s.empty()) {p = s.pop();s2.push(p);if(p.left != null)s.push(p.left);if(p.right != null)s.push(p.right);}while(!s2.empty())System.out.print(s2.pop().val + " ");}/** 非递归后续2(设置pre结点) */public static void iterativePos_2(Node T){Node cur,pre = null; Stack<Node>s = new Stack<Node>();s.push(T);while(!s.empty()) {cur = s.peek();if((cur.left == null && cur.right == null) || ((pre != null) && (pre == cur.left || pre == cur.right))) {System.out.print(cur.val + " ");s.pop();pre = cur;}else {if(cur.right != null)s.push(cur.right);if(cur.left != null)s.push(cur.left);}}}public static void levelOrder(Node T) {Queue<Node>q = new LinkedList<Node>();if(T != null) {q.add(T);while(!q.isEmpty()) {Node now = q.poll();System.out.print(now.val + " ");if(now.left != null)q.add(now.left);if(now.right != null)q.add(now.right);}}}//查找某个值为x的结点public static Node search(Node T,int x) {if(T == null) return null;if(T.val == x)return T;else {if(search(T.left,x) == null)  return search(T.right,x);else return search(T.left,x);}}//统计结点个数public static int count(Node T) {if(T != null) {return count(T.left) + count(T.right) + 1;}else return 0;}//计算二叉树的深度public static int depth(Node T) {if(T == null)return 0;int l = depth(T.left);int r = depth(T.right);return l > r ? l + 1 : r + 1;}//判断两棵树是不是相等public static boolean is_SameTree(Node T1,Node T2) {if(T1 == null && T2 == null)return true;else {return T1 != null && T2 != null && T1.val == T2.val&& is_SameTree(T1.left,T2.left) && is_SameTree(T1.right,T2.right);}}public static void main(String[] args) {Scanner cin = new Scanner(new BufferedInputStream(System.in));//     int[] arr = {1,2,3,4,5,6,7,8,-1,9,-1,10,-1,11,-1, -1,-1,-1,-1,-1,-1,-1,-1};int[] arr = {1,2,3,4,5,6,7,8,-1,9,-1,10,-1,11,-1};Node root = createTree(arr,0);//        树结构和上面相同,输入: 1 2 4 8 -1 -1 -1 5 9 -1 -1 -1 3 6 10 -1 -1 -1 7 11 -1 -1 -1Node root2 = buildTree(cin);System.out.println("-------前序遍历-------");preOrder(root);System.out.println();iterativePre(root);System.out.println();iterativePre_2(root);System.out.println("\n" + "-------中序遍历-------");inOrder(root);System.out.println();iterativeIn(root);System.out.println("\n" + "-------后序遍历-------");postOrder(root);System.out.println();iterativePos(root);System.out.println();iterativePos_2(root);System.out.println("\n" + "-------层次遍历-------");levelOrder(root);System.out.println("\n" + "------结点个数-------");System.out.println(count(root));System.out.println("\n" + "------二叉树深度-------");System.out.println(depth(root));System.out.println("\n" + "-----判断两棵树是不是相同-----");System.out.println(is_SameTree(root,root2));System.out.println("\n" + "-----寻找树中有没有值为3的结点-----");Node Find = search(root,3);if(null == Find)System.out.println("没有找到结点");elseSystem.out.println("这个结点的左右子结点的值是" + Find.left.val + " " + Find.right.val);}
}

运行效果如下图:

二叉树的各种操作(递归和非递归遍历,树深度,结点个数等等)相关推荐

  1. 树与二叉树的深度优先与广度优先算法(递归与非递归)

    本博客前面文章已对树与二叉树有过简单的介绍,本文主要是重点介绍有关二叉树的一些具体操作与应用 阅读本文前,可以先参考本博客 各种基本算法实现小结(三)-- 树与二叉树   和  各种基本算法实现小结( ...

  2. 二叉树的链式结构的非递归遍历

    二叉树的链式结构的非递归遍历 一. 非递归前序遍历和非递归中序遍历 1.    Stack.h #ifndef__STACK_H__ #define__STACK_H__ #include<st ...

  3. 剑指offer之中判断二叉树是不是对称二叉树(递归和非递归实现)

    1 问题 判断二叉树是不是对称(递归和非递归实现) 如下二叉树,就是对称的二叉树 23 3 1 4 4 1 如下二叉树,就是非对称的二叉树 23 3 1 4 4 2 2 代码实现 #include & ...

  4. 二叉树的遍历-递归与非递归 - 海子

    二叉树的遍历-递归与非递归 二叉树是一种非常重要的数据结构,很多其它数据结构都是基于二叉树的基础演变而来的.对于二叉树,有前序.中序以及后序三种遍历方法.因为树的定义本身就是递归定义,因此采用递归的方 ...

  5. 有关二叉树的相关实现:建树,遍历(递归与非递归实现)

    首先定义二叉树的节点 struct BTNode {int data;BTNode *left;BTNode *right; }; 然后先序建立二叉树 思路:以数组中的元素先序构建二叉树,过程就是不断 ...

  6. java 反转二叉树 非递归_【刷算法】翻转二叉树的递归和非递归解法

    题目描述 操作给定的二叉树,将其变翻转为源二叉树的镜像. 输入描述: 1 1 / \ / \ 2 3 ------> 3 2 / \ / \ / \ / \ 4 5 6 7 7 6 5 4 解题 ...

  7. 二叉树,二叉树的归先序遍历,中序遍历,后序遍历,递归和非递归实现

    二叉树,二叉树的归先序遍历,中序遍历,后序遍历,递归和非递归实现 提示:今天开始,系列二叉树的重磅基础知识和大厂高频面试题就要出炉了,咱们慢慢捋清楚! 文章目录 二叉树,二叉树的归先序遍历,中序遍历, ...

  8. 二叉树的三种遍历方式(递归、非递归和Morris遍历)

    二叉树的三种遍历方式(递归.非递归和Morris遍历) 原文:http://www.linuxidc.com/Linux/2015-08/122480.htm 二叉树遍历是二叉树的最基本的操作,其实现 ...

  9. 【Java数据结构】二叉树的前中后序遍历(递归和非递归)

    二叉树的遍历 递归做法 前序遍历 中序遍历 后序遍历 非递归 前序遍历 中序遍历 后序遍历 二叉树遍历是二叉树的一种重要操作 必须要掌握 二叉树的遍历可以用递归和非递归两种做法来实现 递归做法 前序遍 ...

最新文章

  1. UVa213 - Message Decoding
  2. PAT甲级1023 Have Fun with Numbers:[C++题解]高精度加法和两个vector大小比较
  3. requests基础3
  4. VTK:可视化之Camera
  5. 国科大高级人工智能8-归结原理和horn子句
  6. imsi序列号_IPhone 获取IMSI序列号
  7. 加密2-华东师范-2020
  8. 温故而知新,8个有用的JS技巧「小知识点」
  9. Eclipse—在Eclipse中如何发布创建的JavaWeb工程
  10. Prescan(七):prescan中air传感器的配置
  11. centos安装思源黑体
  12. 知识图谱研究最新综述论文: 表示学习、知识获取与应用
  13. 配置SecureCRT密匙登录
  14. 朴素贝叶斯与贝叶斯信念网络
  15. 60条有名的处世原则与定理
  16. 一、一个月有多少天(Biweekly4)
  17. 【小迪安全】web安全|渗透测试|网络安全 | 学习笔记-7
  18. Simulink如何添加模块到Library Browser
  19. 5码默认版块_短说社区论坛系统版块权限功能
  20. 赢在微点答案专区英语_英语u校园读写2答案unit3,u校园新标准大学英语视听说3单元测试答案搜题公众号...

热门文章

  1. ZZULIOJ:1008 美元和人民币
  2. 【百套源码】HTML5期末大作业 - 各类网页作业源码合集
  3. 如何删掉计算机里的网络驱动器,电脑问题电脑目录多2个cd驱动器,怎么删除 – 手机爱问...
  4. BZOJ 4480: [Jsoi2013]快乐的jyy(回文自动机)
  5. TeamSpeak服务器无法运行,如何在启动时运行TeamSpeak3服务器?
  6. Intent之Action详解
  7. mass Framework switchable插件
  8. Web报表系统葡萄城报表:报表软件
  9. java温度传感器用法_结合Android JNI和前端的温度传感器测温案例
  10. Python-因式分解 (10 分)