Hello everyone, this is Alex. As we know, a tree is a frequently-used data structure to simulate a hierarchical tree structure.One reason to use trees might be because you want to store information that naturally forms a hierarchy.
Also there are some other applications using trees:
Manipulate hierarchical data.
Make information easy to search (see tree traversal).
Manipulate sorted lists of data.
As a workflow for compositing digital images for visual effects.
Router algorithms
Form of a multi-stage decision-making (see business chess).
A Binary Tree is one of the most typical tree structure. I have posted [Binary Tree] which include some significant information definitions and manipulation of [Binary Tree]. Please check it before viewing this article. Here are some common Binary Tree Leetcode problems I have sorted them out.
By completing these problems, you will be able to:
Deeply understand the concept of a binary tree;
Be familiar with different traversal methods;
Use recursion to solve binary-tree-related problems;
Introduction to Recursion The process in which a function calls itself directly or indirectly is called recursion and the corresponding function is called a recursive function.
A recursive algorithm takes one step toward solution and then recursively call itself to further move. The algorithm stops once we reach the solution.
Since called function may further call itself, this process might continue forever. So it is essential to provide a base case to terminate this recursion process.
Steps to Implement Recursion:
Step 1:Determine the parameters and return values of recursive functions Parameters are pointers to the nodes that need to be passed in, and no other parameters are needed. Usually, the main parameters are set at this time. If other parameters are found to be needed in writing recursive logic, they can be added at any time. In fact, there is no need to return a value, but the pointer given in the question to return the root node can be directly used with the function defined in the question, so the return type of the function is TreeNode.
1
public TreeNode invertTree(TreeNode root)
Step 2:Determine termination conditions When the current node is empty then return.
1
if (root == NULL) return root;
Each recursive call allocates a new Stack Frame in the Call Stack to store the local variables, parameters, and return address of the current function. If there is no termination condition, recursion will continue indefinitely, stack frames will pile up infinitely, and eventually exceed the JVM’s stack space limit —> leading to Stack overflow Error.
Step 3:Determine the logic of single-layer recursion It’s very important to detremine what the logic of single-layer recursion: 4. Tree Traversal Techniques(Tree Traversal Techniques) like PreOder, PostOrder and InOrder to solve the problem respectively.
//DFS classSolution { /** * Both PreOder and PostOder traversal are correct. *Inorder is not possible, because first the left child is exchanged for *another child, then the root child is exchanged (after completion, the right *child has become the original left child), and then the right child is *exchanged for another child (at this point, it is actually exchanging for the *original left child) */ public TreeNode invertTree(TreeNode root) { if (root == null) { returnnull; } invertTree(root.left); invertTree(root.right); swapChildren(root); return root; }
//To compare two nodes with different values, first clarify the situation where the two nodes are empty! Otherwise, when comparing numerical values later, the null pointer will be manipulated. if (left == null && right != null) { returnfalse; } if (left != null && right == null) { returnfalse; }
if (left == null && right == null) { returntrue; } if (left.val != right.val) { returnfalse; } //TheThe logic of single-layer recursion is to handle situations where both left and right nodes are not empty and have the same value. // Compare outside booleancompareOutside= compare(left.left, right.right); // Compare inside booleancompareInside= compare(left.right, right.left); return compareOutside && compareInside; }
//DFS classSolution { /** * The minimum depth is the number of nodes along the shortest path from the * * root node down to the nearest leaf nod */ publicintminDepth(TreeNode root) { if (root == null) { return0; } intleftDepth= minDepth(root.left); intrightDepth= minDepth(root.right); if (root.left == null) { return rightDepth + 1; } if (root.right == null) { return leftDepth + 1; } // Both left and right nodes are not null return Math.min(leftDepth, rightDepth) + 1; } }
//BFS classSolution { publicintminDepth(TreeNode root) { if (root == null) { return0; } Deque<TreeNode> deque = newLinkedList<>(); deque.offer(root); intdepth=0; while (!deque.isEmpty()) { intsize= deque.size(); depth++; for (inti=0; i < size; i++) { TreeNodepoll= deque.poll(); if (poll.left == null && poll.right == null) { // It is a leaf node that directly returns depth. Because it is traversed from top to bottom, this value is the minimum value return depth; } if (poll.left != null) { deque.offer(poll.left); } if (poll.right != null) { deque.offer(poll.right); } } } return depth; } }
classSolution { publicintcountNodes(TreeNode root) { if(root == null) { return0; } // we need use postorder traversal to first calculate left node and then right node and then the return countNodes(root.left) + countNodes(root.right) + 1; } }
classSolution { publicintcountNodes(TreeNode root) { if (root == null) return0; Queue<TreeNode> queue = newLinkedList<>(); queue.offer(root); intresult=0; while (!queue.isEmpty()) { intsize= queue.size(); while (size -- > 0) { TreeNodecur= queue.poll(); result++; if (cur.left != null) queue.offer(cur.left); if (cur.right != null) queue.offer(cur.right); } } return result; } }
privateintgetHeight(TreeNode root) { if (root == null) { return0; } intleftHeight= getHeight(root.left); if (leftHeight == -1) { return -1; } intrightHeight= getHeight(root.right); if (rightHeight == -1) { return -1; } // If the height difference between the left and right subtrees is greater than 1, return -1 indicates that it is no longer a balanced tree if (Math.abs(leftHeight - rightHeight) > 1) { return -1; } return Math.max(leftHeight, rightHeight) + 1; } }
Binary Tree Paths
Analysis:This question requires a path from the root node to the leaf, so it needs to be traversed in PreOrder to make it easier for the parent node to point to the child node and find the corresponding path.
classSolution { public List<String> binaryTreePaths(TreeNode root) { List<String> res = newArrayList<>(); if (root == null) { return res; } List<Integer> paths = newArrayList<>(); traversal(root, paths, res); return res; }
privatevoidtraversal(TreeNode root, List<Integer> paths, List<String> res) { paths.add(root.val);// PreOder--> mid // If we find the leaf node then we need to collect one possible path if (root.left == null && root.right == null) { // StringBuilder is used to concatenate strings, which is faster StringBuildersb=newStringBuilder(); for (inti=0; i < paths.size() - 1; i++) { sb.append(paths.get(i)).append("->"); } // Add last node since we don't need -> sb.append(paths.get(paths.size() - 1)); res.add(sb.toString());//add one path return; } // Recursive and backtracking occur simultaneously, so they should be placed in the same curly braces if (root.left != null) { // left traversal(root.left, paths, res); paths.remove(paths.size() - 1);// BackTracking } if (root.right != null) { // right traversal(root.right, paths, res); paths.remove(paths.size() - 1);// Backtracking } } }
//BFS classSolution { public List<String> binaryTreePaths(TreeNode root) { List<String> result = newArrayList<>(); if (root == null) return result; Stack<Object> stack = newStack<>();
stack.push(root); stack.push(root.val + ""); while (!stack.isEmpty()) { Stringpath= (String) stack.pop(); TreeNodenode= (TreeNode) stack.pop(); // leaf node if (node.left == null && node.right == null) { result.add(path); } //the right child node is not null if (node.right != null) { stack.push(node.right); stack.push(path + "->" + node.right.val); } //the left child node is not null if (node.left != null) { stack.push(node.left); stack.push(path + "->" + node.left.val); } } return result; } }