Preface
The related concepts of binary trees are also not introduced here. After all, the basic data structure has been discussed a lot, so it is not repeated here.For some balanced binary trees, complete binary trees, red-black trees, B+trees and other related structures, this has been introduced by many blogs, here is just some basic operations of binary trees.
Definition
This should have been seen many times
/** * autor:liman * createtime:2020/2/6 * comment:Implementation of Binary Tree Node */ public class TreeNode { public String value; public TreeNode left; public TreeNode right; public TreeNode(String value) { this.value = value; } }
Traversed non-recursive implementation
Recursive implementations are very simple, and many people are expected to do so. Here's a description of non-recursive traversal implementations
The basic traversal operation is as follows:The output shown here represents the traversal operation
/** * Do traversal. * * @param node */ public static void doTraverse(TreeNode node) { System.out.print(node.value + " "); }
Preorder traversal
/** * Priority traversal of non-recursive implementations, where stack operations are used * * @param root */ public static void preOrderTraverse(TreeNode root) { if (root != null) {//If the root node is not empty Stack<TreeNode> stack = new Stack<>();//Non-recursive to use stack stack.push(root); while (!stack.isEmpty()) { TreeNode node = stack.pop(); doTraverse(node); //Judge the right subtree before the left subtree if (node.right != null) {//Judges the right subtree, if it is not empty, stacks stack.push(node.right); } if (node.left != null) {//Judge left subtree, if left subtree is not empty then stack stack.push(node.left); } } } }
Intermediate traversal
/** * Ordered traversal in a non-recursive implementation * * @param root */ public static void innerOrderTraverse(TreeNode root) { if (root != null) { Stack<TreeNode> stack = new Stack<>(); //Continuously stack the left subtree to find the leftmost node in the whole tree (the first node traversed in middle order) while (!stack.isEmpty() || root != null) { if (root != null) { stack.push(root); root = root.left; } else {//If the left subtree of the node is empty, it pops up, traverses, and points to the right subtree. root = stack.pop(); doTraverse(root); root = root.right; } } } }
Subsequent traversal
/** * Non-recursive implementation of post-traversal, one stack is cumbersome, two stacks are relatively simple * * @param root */ public static void postOrderTraverse(TreeNode root) { if (root != null) { Stack<TreeNode> stack1 = new Stack<>(); Stack<TreeNode> stack2 = new Stack<>();//Store elements to traverse stack1.push(root); while (!stack1.isEmpty()) { TreeNode pop = stack1.pop(); stack2.push(pop); if (pop.left != null) { stack1.push(pop.left); } if (pop.right != null) { stack1.push(pop.right); } } while (!stack2.isEmpty()) { doTraverse(stack2.pop()); } } }
level traversal
/** * Hierarchical traversal, non-recursive implementation * Take advantage of queues * @param root */ public static void levelOrderTraverse(TreeNode root) { if (root != null) { Queue<TreeNode> queue = new ArrayDeque<>(); queue.offer(root);//root Queued while (!queue.isEmpty()) { //Get the number of nodes at the current level int levelNum = queue.size(); for (int i = 0; i < levelNum; i++) { TreeNode node = queue.poll(); doTraverse(node); if (node.left != null) {//Queued Left Subtree queue.offer(node.left); } if (node.right != null) {//Queue Right Subtree queue.offer(node.right); } } } } }
Calculating the Depth of a Binary Tree (Maximum Depth and Minimum Depth)
Maximum Depth
Recursive implementation
Recursive maximum depth, code is simplest
/** * Recursive calculation of maximum depth * * @param root * @return */ public int maxDepth(TreeNode root) { if (root == null) { return 0; } int left = maxDepth(root.left); int right = maxDepth(root.right); return Math.max(left, right) + 1;//After all, the root node is one level high. }
Non-recursive implementation
/** * Non-recursive maximum depth, using hierarchical traversal * * @param root * @return */ public int maxDepthLevel(TreeNode root) { if (root == null) { return 0; } Queue<TreeNode> queue = new ArrayDeque<>(); queue.offer(root); int level = 0; while (!queue.isEmpty()) { level++; //Here the statistical level is high int levelNum = queue.size(); for (int i = 0; i < levelNum; i++) { TreeNode node = queue.poll(); if (node.left != null) { queue.offer(node.left); } if (node.right != null) { queue.offer(node.right); } } } return level; }
Minimum Depth
The minimum depth is more than the maximum depth, returning 0 directly if the tree is empty, 1 directly if there is only one root node, and the minimum depth of the right subtree if the left subtree is empty and the right subtree is not empty.If the right subtree is empty and the left subtree is not empty, the minimum depth of the left subtree needs to be calculated.
Recursive implementation
/** * Recursively find the minimum depth of a tree, there are more conditions to consider here * * @param root * @return */ public int mixDepth(TreeNode root) { if (root == null) {//Tree is empty return 0; } if (root.left == null && root.right == null) {//Only one root node return 1; } if (root.left == null && root.right != null) {//Left subtree is empty, right subtree is not empty return mixDepth(root.right) + 1; } if (root.right == null && root.left != null) {//Right subtree is empty, left subtree is not empty return mixDepth(root.left) + 1; } //This is when neither the left subtree nor the right subtree is empty int left = mixDepth(root.left); int right = mixDepth(root.right); return Math.min(left,right)+1; }
Non-recursive implementation
When traversing through a hierarchy, the height of the first node that encounters an empty left and right subtree must be the minimum depth
public int mixDepthLevel(TreeNode root){ if (root == null) { return 0; } Queue<TreeNode> queue = new ArrayDeque<>(); queue.offer(root); int level = 0; while (!queue.isEmpty()) { level++; int levelNum = queue.size(); for (int i = 0; i < levelNum; i++) { TreeNode node = queue.poll(); //The height of the first node with empty left and right subtrees must be the minimum depth if(node.left== null && node.right == null){ return level; } if (node.left != null) { queue.offer(node.left); } if (node.right != null) { queue.offer(node.right); } } } return 0; }
Compute the nearest ancestor of two nodes
/** * Recursively implements the closest ancestor of two nodes * @param root * @param node01 * @param node02 * @return */ public TreeNode lowestCommoAncestor(TreeNode root, TreeNode node01, TreeNode node02) { //The closest common ancestor is the root node if (root == null || root == node01 || root == node02) { return root; } TreeNode left = lowestCommoAncestor(root.left, node01, node02); TreeNode right = lowestCommoAncestor(root.right, node01, node02); if (left != null && right != null) {//If left and right are not empty, both sides can find node01 and node02, then the public node can only root return root; } //If only one is empty, then that common ancestor is this node. return left != null ? left : right; }
Constructing Binary Trees from Median and Priority Sequences
In fact, this data structure has been exposed to related problems, that is, to find the root node from the sequence of sequential traversal, and then this root node is the left subtree of the node to the left of the sequence, and the nodes to the right of the sequence are all the right subtrees of the node.
public TreeNode buildTree(int[] preOrder,int[] inOrder){ if(preOrder == null || inOrder == null){ return null; } HashMap<Integer,Integer> map = new HashMap<>(); for(int i=0;i<inOrder.length;i++){ map.put(inOrder[i],i); } return buildTree(preOrder,0,preOrder.length-1,inOrder,0,inOrder.length-1,map); } /** * Construct a binary tree from an array traversed in middle and first order * @param preOrder Priority Sequence Array * @param pStart Starting position of the sequence * @param pEnd End position of sequence * @param inOrder Median sequence array * @param iStart The starting position of an intermediate sequence * @param iEnd End position of intermediate sequence * @param map map of ordered sequence elements and index locations, just for ease of search * @return */ public TreeNode buildTree(int[] preOrder,int pStart,int pEnd, int[] inOrder,int iStart,int iEnd, HashMap<Integer,Integer> map){ if(pStart>pEnd || iStart>iEnd){ return null; } TreeNode head = new TreeNode(preOrder[pStart]); int index = map.get(preOrder[pStart]);//Locate the root node in the middle traversal head.left = buildTree(preOrder,pStart+1,pStart+index-iStart,inOrder,iStart,index-1,map); head.right = buildTree(preOrder,pStart+index-iStart+1,pEnd,inOrder,index+1,iEnd,map); return head; }
There are a lot of variables here, about the index, which is represented directly by a graph (as for the complete tree structure, you can convert it yourself, this is not difficult):
summary
Some basic operations are summarized. In fact, they have been implemented in C before, but they are not very systematic.