目录
一.概念
二.构建二叉树节点类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
进程已结束,退出代码0
4.非递归遍历
我们可以使用栈(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进程已结束,退出代码0
5.递归求最小深度
/*** 递归求最小深度* @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