目录
一.概念
二.构建二叉树节点类TreeNode
三.二叉树的遍历
1.前序遍历preOrder
2.中序遍历medOrder
3.后序遍历postOrder
4.非递归遍历
三.深度
1.概念
2.递归求最大深度
3.层序遍历加队列求最大深度
4.测试
5.递归求最小深度
6.层序遍历加队列求最小深度
7.测试
四.对称二叉树
五.翻转二叉树
六.后缀表达式构造树
七.根据前序遍历和中序遍历还原树
八.根据后序和中序遍历还原树
一.概念
二叉树是一种树状结构,其中每个节点最多有两个子节点,被称为左子节点和右子节点。二叉树通常具有以下特点:
-  根节点:二叉树的最顶层节点被称为根节点。它没有父节点,是整个树的起点。 
-  子节点:每个节点最多有两个子节点,分别称为左子节点和右子节点。左子节点在树结构中位于父节点的左侧,右子节点在右侧。 
-  叶节点:没有子节点的节点被称为叶节点,也被称为终端节点。叶节点位于树的最底层。 
-  父节点:每个节点的上一层节点被称为父节点。每个节点除了根节点都有一个父节点。 
-  兄弟节点:拥有相同父节点的节点被称为兄弟节点。 
-  深度:节点的深度是指从根节点到该节点的路径上的节点数。 
-  高度:节点的高度是指从该节点到树的最底层叶节点的最长路径。 
-  子树:节点及其子节点以及与之相关的边所构成的树称为子树。 
二叉树在计算机科学中具有广泛的应用,例如在搜索树和排序算法中的使用。因为二叉树具有简单的结构和快速的搜索能力,所以它被广泛应用于许多领域
二.构建二叉树节点类TreeNode
package 树.二叉树;/*** 普通二叉树*/
public class TreeNode {int value;TreeNode left;TreeNode right;public TreeNode(int value,TreeNode left,TreeNode right){this.value = value;this.left = left;this.right = right;}@Overridepublic String toString() {return "TreeNode{" +"value=" + value +", left=" + left +", right=" + right +'}';}
}
三.二叉树的遍历
1.前序遍历preOrder
    /*** 前序遍历** @param treeNode*/static void preOrder(TreeNode treeNode) {if (treeNode == null) {return;}System.out.print(treeNode.value + "\t");preOrder(treeNode.left);    //左preOrder(treeNode.right);   //右}2.中序遍历medOrder
    /*** 中序遍历** @param treeNode*/static void medOrder(TreeNode treeNode) {if (treeNode == null) {return;}medOrder(treeNode.left);    //左System.out.print(treeNode.value + "\t");medOrder(treeNode.right);   //右}3.后序遍历postOrder
    /*** 后序遍历** @param treeNode*/static void postOrder(TreeNode treeNode) {if (treeNode == null) {return;}postOrder(treeNode.left);    //左postOrder(treeNode.right);   //右System.out.print(treeNode.value + "\t");}分别打印一下:
 public static void main(String[] args) {/***            1*           / \*          2   3*         /   / \*        6   9   8*/TreeNode root = new TreeNode(1,new TreeNode(2, new TreeNode(6, null, null), null),new TreeNode(3, new TreeNode(9, null, null), new TreeNode(8, null, null)));System.out.println("前序遍历");preOrder(root);System.out.println();System.out.println("中序遍历");medOrder(root);System.out.println();System.out.println("后序遍历");postOrder(root);}运行:
前序遍历
1	2	6	3	9	8	
中序遍历
6	2	1	9	3	8	
后序遍历
6	2	9	8	3	1	
进程已结束,退出代码04.非递归遍历
我们可以使用栈(stack)来实现二叉树的遍历,栈可以模拟递归
 /*** 通过迭代实现三种遍历*/@Testpublic void testAllOrder(){/***            1*           / \*          2   3*         /   / \*        6   9   8*/TreeNode root = new TreeNode(1,new TreeNode(2, new TreeNode(6, null, null), null),new TreeNode(3, new TreeNode(9, null, null), new TreeNode(8, null, null)));//创建栈LinkedListStack<TreeNode> stack = new LinkedListStack<>(10);//定义指针变量,初始指向root节点TreeNode node = root;TreeNode pop = null;//遍历,只要node不为空,并且栈中有元素while (node != null || !stack.isEmpty()) {//先找左孩子if (node != null) {//压入栈stack.push(node);System.out.println("前序:"+node.value);//指向左孩子node = node.left;} else {//弹出元素TreeNode peek = stack.peek();// 没有右子树if (peek.right == null ) {System.out.println("中序遍历:"+peek.value);//弹出pop = stack.pop();System.err.println("后序:"+pop.value);}//右子树处理完成else if ( peek.right == pop) {pop = stack.pop();System.err.println("后序:"+pop.value);}//待处理右子树else {System.out.println("中序遍历:"+peek.value);//指向栈顶元素的右孩子node = peek.right;}}}}测试一下:
前序:1
前序:2
前序:6
后序:6
中序遍历:6
后序:2
中序遍历:2
后序:9
中序遍历:1
后序:8
前序:3
前序:9
中序遍历:9
后序:3
中序遍历:3
后序:1
前序:8
中序遍历:8三.深度
1.概念
二叉树的深度是指从根节点到最远叶子节点的路径上的节点个数。可以通过递归或迭代的方式来计算二叉树的深度。
递归方法:
- 如果二叉树为空,返回深度为 0。
- 否则,分别计算左子树和右子树的深度。
- 树的深度为左子树深度和右子树深度中的较大值加 1。
迭代方法(层次遍历):
- 如果根节点为空,返回深度为 0。
- 创建一个队列,并将根节点入队。
- 初始化深度为 0。
- 循环执行以下步骤直到队列为空: - 获取当前层的节点个数,记为 count。
- 将当前层的节点依次出队,并将它们的左子节点和右子节点依次入队。
- 将 count 减去当前层节点个数,如果 count 大于 0,则深度加 1。
 
- 返回深度的值。
无论使用递归还是迭代的方式,都可以得到二叉树的深度。
2.递归求最大深度
/*** 递归调研求最大深度* @param node* @return*/static int maxDepth(TreeNode node){if(node == null){return 0;}if(node.left == null && node.right == null){return 1;}int d1 = maxDepth(node.left);int d2 = maxDepth(node.right);return Integer.max(d1,d2) + 1;}3.层序遍历加队列求最大深度
/***  通过层序遍历 加 队列 来实现求最大深度*  思路:*     每次将一层的节点加入到队列中,然后循环,到下一层取出队列中的元素,如果该*     元素右左右子树,那么继续加入到队列中去,* @param root* @return*/static  int maxDepthByQueue(TreeNode root){if(root==null){return 0;}//创建队列Queue<TreeNode> queue = new LinkedList<>();//先把根节点加入到队列中去queue.offer(root);//初始深度为0int depth = 0;//当队列不为空时循环while (!queue.isEmpty()){//相当于每一层的个数for (int i =0;i < queue.size(); i++){//从队头取出元素TreeNode poll = queue.poll();//如果有左孩子,把左孩子加入到队列中if(poll.left != null){queue.offer(poll.left);}//如果有右孩子,把右孩子加入到队列中if(poll.right != null){queue.offer(poll.right);}}//每遍历完一层,让深度加一depth++;}return depth;}4.测试
    /***          1*         / \*        2   3*      /      \*     4        5*               \*                6**    左子树最大深度为3,右子树为4,所以最大深度为4*/public static void main(String[] args) {TreeNode root = new TreeNode(1,new TreeNode(2,new TreeNode(4,null,null),null),new TreeNode(3,null,new TreeNode(5,null,new TreeNode(6,null,null))));System.out.println("递归法:");System.out.println(maxDepth(root));System.out.println("层序队列法:");System.out.println(maxDepthByQueue(root));}运行:
递归法:
4
层序队列法:
4进程已结束,退出代码05.递归求最小深度
    /*** 递归求最小深度* @param root* @return*/static int minDepth(TreeNode root){if(root == null){return 0;}int d1 = minDepth(root.left);int d2 = minDepth(root.right);//为了防止单边树,如果一边为null,那么应该返回另一边的深度if(d1 == 0){return d2 + 1;}if(d2 == 0){return d1 + 1;}return Integer.min(d1,d2) + 1;}6.层序遍历加队列求最小深度
 /*** 层序遍历找最小深度,*  思路:*    如果找到第一个叶子节点,那么该叶子节点所在的层就是最小层* @param root* @return*/static int minDepthFloorByQueue(TreeNode root){if(root==null){return 0;}LinkedList<TreeNode> queue = new LinkedList<>();int depth = 0;queue.offer(root);while (!queue.isEmpty()){int size = queue.size();depth++;for (int i = 0; i < size; i++) {TreeNode poll = queue.poll();if(poll.left==null && poll.right==null){return depth;}if(poll.left!=null){queue.offer(poll.left);}if(poll.right != null){queue.offer(poll.right);}}}return depth;}
}7.测试
 public static void main(String[] args) {/***          1*         / \*        2   3*      /      \*     4        5*               \*                6**    左子树最大深度为3,右子树为4,所以最大深度为4*/TreeNode root = new TreeNode(1,new TreeNode(2,new TreeNode(4,null,null),null),new TreeNode(3,null,new TreeNode(5,null,new TreeNode(6,null,null))));System.out.println("递归法:");System.out.println(minDepth(root));System.out.println("层序队列法:");System.out.println(minDepthFloorByQueue(root));}运行:
递归法:
3
层序队列法:
3进程已结束,退出代码0四.对称二叉树
/*** 对称二叉树*/
public class D_Eq_Tree {/***   左的左和右的右相等,*   左的右和右的左相等** @param args*/public static void main(String[] args) {/***           1*          / \*         2   2*        /\   /\*       3  4 4  3*/TreeNode root =   new TreeNode(1,new TreeNode(2,new TreeNode(3,null,null),new TreeNode(4,null,null)),new TreeNode(2,new TreeNode(4,null,null),new TreeNode(3,null,null)));boolean flag = isD_EqTree(root.left, root.right);System.out.println(flag);}static boolean isD_EqTree(TreeNode left,TreeNode right){//如果左子树和右子树都为null,则对称if(left==null && right==null){return true;}//如果左子树或者右子树为Null,则不对称if(left==null || right==null){return false;}//如果左右两边值不相等,则不对称if(left.value != right.value){return false;}//左右递归return isD_EqTree(left.left,right.right) && isD_EqTree(left.right,right.left);}}测试运行:
true进程已结束,退出代码0五.翻转二叉树
/*** 翻转二叉树*/
public class L226_ReversedTree {public static void main(String[] args) {/***         1                       1*        / \                     / \*       2   3        =>         3   2*      / \ / \                 / \ / \*     4  5 6  7               7  6 5 4*/TreeNode root = new TreeNode(1,new TreeNode(2,new TreeNode(4,null,null),new TreeNode(5,null,null)),new TreeNode(3,new TreeNode(6,null,null),new TreeNode(7,null,null)));preOrder(root);System.out.println();reversed(root);preOrder(root);}/*** 翻转二叉树* @param root*/static void reversed(TreeNode root){if(root==null){return;}TreeNode node = root.left;root.left = root.right;root.right = node;//递归左子树reversed(root.left);//递归右子树reversed(root.right);}static void preOrder(TreeNode root){if(root==null){return;}System.out.print(root.value+"\t");preOrder(root.left);preOrder(root.right);}}运行:
1	2	4	5	3	6	7	
1	3	7	6	2	5	4	
进程已结束,退出代码0六.后缀表达式构造树
/*** 后缀表达式构造树*/
public class Last_Expression {/***     中缀: (2-1)*3*     后缀:  21-3***     树:*              **             / \*            -   3*           / \*          2   1**    后序遍历就能获得 : 21-3**** @param args*/public static void main(String[] args) {String[] str = {"2","1","-","3","*"};LinkedList<TreeStrNode> stack = new LinkedList<>();for (String c : str) {switch (c){case "+":case "-":case "*":case "/"://先弹出的为右孩子TreeStrNode right = stack.pop();//后弹出的为左孩子TreeStrNode left = stack.pop();//创建树TreeStrNode parent = new TreeStrNode(c);parent.left = left;parent.right = right;//最后把父节点压入栈stack.push(parent);break;default://如果不是运算符,就压入栈stack.push(new TreeStrNode(c));}}//最终栈中的节点为树的根节点TreeStrNode root = stack.peek();//对他做一个后序遍历postOrder(root);}/*** 后序遍历** @param treeNode*/static void postOrder(TreeStrNode treeNode) {if (treeNode == null) {return;}postOrder(treeNode.left);    //左postOrder(treeNode.right);   //右System.out.print(treeNode.str + "\t");}}运行:
2	1	-	3	*	
进程已结束,退出代码0七.根据前序遍历和中序遍历还原树
/*** 根据前序遍历和中序遍历 还原树*/
public class Pre_In_ToTree {/***            1*           / \*          2   3*         /   / \*        6   9   8**        preOrder = 1, 2, 6, 3, 9, 8*        inOrder = 6, 2, 1, 9 , 3, 8**  思路:*     前序遍历的第一个是根节点 root = 1*     然后找到中序遍历中 root所在的位置*     ->左边的是左子树*     ->右边的是右子树*     中序:*      左: 6,2*      右: 9,3,8*     前序:*      左: 2,6*      右: 3,9,8**     这样根据中序的左和前序的左,可以知道 6 是左子树, 2 是父节点*         根据中序的右和前序的右,可以知道 9是左子树,8是右子树,3是父节点**     最后递归调用继续分割左右数组***/public static void main(String[] args) {int[] preOrder = {1, 2, 6, 3, 9, 8};int[]  inOrder = {6, 2, 1, 9 , 3, 8};TreeNode root = getTree(preOrder, inOrder);preOrder(root);}static TreeNode getTree(int[] preOrder,int[] inOrder){//结束递归条件if(preOrder.length == 0){return null;}//先找到根节点int rootValue = preOrder[0];TreeNode root = new TreeNode(rootValue, null, null);//在中序中找到根节点的位置for (int i = 0; i < inOrder.length ; i++) {if(inOrder[i] == rootValue){//找到之后切割左子树和右子树//inOrder.left = 0 ~ i-1//inOrder.right = i+1 ~ inOrder.length-1;int[] inLeft = Arrays.copyOfRange(inOrder, 0, i);int[] inRight = Arrays.copyOfRange(inOrder, i + 1, inOrder.length);//继续切割preOrder//preOrder.left = 1 ~ i+1//preOrder.right = i+2 ~ preOrder.length-1int[] preLeft = Arrays.copyOfRange(preOrder, 1, i+1);int[] preRight = Arrays.copyOfRange(preOrder, i + 1, preOrder.length);//递归调用root.left = getTree(preLeft,inLeft);root.right = getTree(preRight,inRight);break;}}return root;}/*** 前序遍历** @param treeNode*/static void preOrder(TreeNode treeNode) {if (treeNode == null) {return;}System.out.print(treeNode.value + "\t");preOrder(treeNode.left);    //左preOrder(treeNode.right);   //右}}运行:
1	2	6	3	9	8	
进程已结束,退出代码0
八.根据后序和中序遍历还原树
/*** 根据后序和中序遍历还原树*/
public class Post_In_ToTree {/***            1*           / \*          2   3*         /   / \*        6   9   8**        postOrder = 6,2,9,8,3,1*        inOrder = 6, 2, 1, 9 , 3, 8***/public static void main(String[] args) {int[] postOrder = {6,2,9,8,3,1};int[]  inOrder = {6, 2, 1, 9 , 3, 8};TreeNode root = buildTree(postOrder, inOrder);preOrder(root);}static TreeNode buildTree(int[] postOrder,int[] inOrder){if(postOrder.length==0){return null;}//先找根节点int rootValue = postOrder[postOrder.length - 1];TreeNode root = new TreeNode(rootValue, null, null);for (int i = 0; i < inOrder.length; i++) {if(inOrder[i] == rootValue){//切分int[] inLeft = Arrays.copyOfRange(inOrder, 0, i);int[] inRight = Arrays.copyOfRange(inOrder, i + 1, inOrder.length);int[] postLeft = Arrays.copyOfRange(postOrder, 0, i);int[] postRight = Arrays.copyOfRange(postOrder, i , postOrder.length - 1);//递归root.left = buildTree(postLeft,inLeft);root.right = buildTree(postRight,inRight);break;}}return root;}}运行:
1	2	6	3	9	8	
进程已结束,退出代码0