No longer afraid! Binary tree algorithm related summary | Force program

Author | BoCong-Deng

Source | CSDN blog, Zebian | yugao

Exhibition | CSDN (ID: CSDNnews)

EDITORIAL

For programmers, the tree should not be unfamiliar, especially binary tree, as long as the basic contact algorithm of this kind are bound to run into, so I'm going to, the relevant binary tree algorithm is summarized by a summary article, ideas and code to achieve the combination, so that you are not afraid of a binary tree.

preparation

The following ideas to explain, I will give the idea of ​​a class of pseudo-code, and then the instructions, that is an idea framework, with the idea framework, after the encounter the problem directly to the frame is complete. This article will mainly talk about binary search tree (Binary Search Tree, referred to as BST), BST is a very common binary tree. It is defined as: a binary tree, the value of any node is greater than or equal to the value of the left subtree of all nodes, and less than or equal to the values ​​of all nodes to the right subtree. Here's what meets the definition of a BST:

If you have a special idea behind the structure, such as multi-tree, I will be specified. First, let's give definition of binary tree nodes (this definition should not be unfamiliar to you, brush algorithm problem he comes across).

1public class TreeNode {
2      int val;
3      TreeNode left;
4      TreeNode right;
5      TreeNode(int x) { val = x; }
6}

Recursion

Though, I want to point out that the position of "want to operate" in pseudo-code, not necessarily placed in my position, the position of the specific needs of our judgment depending on the actual demand. But because the front after the order of traversal, recursion, enter the time and I need to be the same.

Preorder

Root node traversal, if the root node is empty, returns; otherwise, traversing the root node, then preorder left subtree, then preorder right subtree.

1public void preorderTraverse(TreeNode root){
2    System.out.print(node.val+" ");
3    preorderTraverse(root.left);
4    preorderTraverse(root.right);
5}

Preorder

Passing the root, if root is null, returns; otherwise, in order to traverse the left subtree, then traverse the root node, then preorder right subtree.

1public void inorderTraverse(TreeNode root){
2    inorderTraverse(root.left);
3    System.out.print(node.val+" ");
4    inorderTraverse(root.right);
5}

Postorder

Passing the root, if root is null, returns; otherwise, the post-order traversal of the left subtree, then the right subtree order traversal, traversing the last root.

1public void postorderTraverse(TreeNode root){
2    postorderTraverse(root.left);
3    postorderTraverse(root.right);
4    System.out.print(node.val+" ");
5}

Iteration (non-recursive)

We use an iterative thinking, in fact, is the use of loops and recursion stack to simulate the operation of recursive above operation, in fact, a constantly about himself and the child node process and push the stack, if you understand the following algorithm of the above algorithms like to understand

Preorder traversal

 1public List<Integer> preorderTraversal(TreeNode root) {
 2    List<Integer> list = new ArrayList<>();
 3    if(root==null){
 4        return list;
 5    }
 6    Stack<TreeNode> stack = new Stack<>();
 7    stack.push(root);
 8    while(!stack.isEmpty()){
 9        TreeNode res = stack.pop();
10        if(res.right != null)
11            stack.push(res.right);
12        if(res.left != null)
13            stack.push(res.left);
14        list.add(res.val);
15
16    }
17    return list;
18}

Preorder

 1public List<Integer> inorderTraversal(TreeNode root) {
 2    List<Integer> list = new ArrayList<>();
 3    if(root==null){
 4        return list;
 5    }
 6    Stack<TreeNode> stack = new Stack<>();
 7    TreeNode curr = root;
 8    while(curr != null || !(stack.isEmpty())){
 9        if(curr!= null){
10            stack.push(curr);
11            curr = curr.left;
12        }else{
13            curr = stack.pop();
14            list.add(curr.val);
15            curr = curr.right;
16        }
17    }
18    return list;
19}

Postorder

We can achieve another very simple traversal: "Root -> Right -> left" traverse. Although this traversal no name, but he is a post-order traversal in reverse order. So we can use two stacks, using the LIFO stack features to enable subsequent traversal.

 1public List<Integer> preorderTraversal(TreeNode root) {
 2    List<Integer> list = new ArrayList<>();
 3    if(root==null){
 4        return list;
 5    }
 6    Stack<TreeNode> stack = new Stack<>();
 7    stack.push(root);
 8    while(!stack.isEmpty()){
 9        TreeNode res = stack.pop();
10        if(res.left != null)
11            stack.push(res.left);
12        if(res.right != null)
13            stack.push(res.right);
14        list.add(res.val);
15
16    }
17    list.reserve();
18    return list;
19}

Depth-first search (DFS)

In fact, the binary tree preorder, preorder, postorder, are the depth-first search, deep search is an idea, not specifically refer to implementation, you can use recursion, you can also use the stack to achieve, so implementation of the above-mentioned are the depth-first search, after all, "depth first" thing.

That is where I mention a few examples of practical applications, to deepen the impression of it.

* The maximum depth of the binary tree

1public int maxDepth(TreeNode root) {
2    if(root==null){
3        return 0;
4    }
5    int left = maxDepth(root.left);
6    int right = maxDepth(root.right);
7    return Math.max(left,right)+1;
8}

* Binary Tree Mirror

 1public void Mirror(TreeNode root) {
 2    if(root!=null){
 3        if(root.left!=null || root.right!= null){
 4            TreeNode temp =root.left;
 5            root.left=root.right;
 6            root.right=temp;
 7        }
 8        Mirror(root.left);
 9        Mirror(root.right);
10    }
11
12}

* Symmetrical binary tree

 1boolean isSymmetrical(TreeNode pRoot){
 2    if(pRoot == null)
 3        return true;
 4    return real(pRoot.left,pRoot.right);
 5}
 6public boolean real(TreeNode root1,TreeNode root2){
 7    if(root1 == null && root2 == null){
 8        return true;
 9    }
10    if(root1 ==null || root2 == null){
11        return false;
12    }
13    if(root1.val != root2.val){
14        return false;
15    }
16    return real(root1.left,root2.right)&&real(root1.right,root2.left);
17}

* Sum of path

 1public class Solution {
 2    private ArrayList<Integer> list = new ArrayList<Integer>();
 3    private ArrayList<ArrayList<Integer>> listAll = new ArrayList<ArrayList<Integer>>();
 4    public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
 5        if(root == null)
 6            return listAll;
 7        list.add(root.val);
 8        target -= root.val;
 9        if(target == 0 && root.left==null && root.right == null){
10            listAll.add(new ArrayList<Integer>(list));
11        }
12        FindPath(root.left,target);
13        FindPath(root.right,target);
14        list.remove(list.size()-1);
15       return listAll;
16    }
17}

* Reconstruction binary tree

 1 public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
 2    return reConstructBinaryTree(pre,0,pre.length-1,in,0,in.length-1);
 3}
 4public TreeNode reConstructBinaryTree(int [] pre,int startpre,int endpre,int [] in,int startin,int endin){
 5    if(startpre > endpre || startin > endin){
 6        return null;
 7    }
 8    TreeNode root = new TreeNode(pre[startpre]);
 9    for(int i =startin;i<=endin;i++){
10        if(in[i] == pre[startpre]){
11            root.left = reConstructBinaryTree(pre,startpre+1,startpre+i-startin,in,startin,i-1);
12            root.right = reConstructBinaryTree(pre,startpre+i-startin+1,endpre,in,i+1,endin);
13        }
14    }
15    return root;
16}

* The last common ancestor binary search tree

 1class Solution {
 2    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
 3        if(root == null || root == p || root == q){
 4            return root;
 5        }
 6        TreeNode left = lowestCommonAncestor(root.left,p,q);
 7        TreeNode right = lowestCommonAncestor(root.right,p,q);
 8        if(left!=null && right!=null){
 9            return root;
10        }
11        return left!=null?left:right;
12    }
13}

* Binary serialization and de-serialization

 1序列化:
 2public String serialize(TreeNode root) {
 3    if (root == null) {
 4        return null;
 5    }
 6    // 利用二叉树的层次遍历方式进行序列化
 7    StringBuilder res = new StringBuilder();
 8    LinkedList<TreeNode> queue = new LinkedList<>();
 9    queue.add(root);
10    while (!queue.isEmpty()) {
11        TreeNode node = queue.remove();
12        if (node != null) {
13            res.append(node.val).append(",");
14            queue.add(node.left);
15            queue.add(node.right);
16        } else {
17            res.append("null,");
18        }
19    }
20    return res.toString();
21}
22反序列化:
23public TreeNode deserialize(String data) {
24    if (data == null || data.length() == 0) {
25        return null;
26    }
27    String[] dataArr = data.split(",");
28    // 层次遍历逆向还原二叉树
29    int index = 0;
30    TreeNode root = toNode(dataArr[index]);
31    LinkedList<TreeNode> queue = new LinkedList<>();
32    queue.add(root);
33    while (index < dataArr.length - 2 && !queue.isEmpty()) {
34        TreeNode cur = queue.remove();
35        // 添加左子节点
36        TreeNode leftNode = toNode(dataArr[++index]);
37        cur.left = leftNode;
38        // 队列中的节点用于为其赋值孩子节点,若该节点本身为 null,
39        // 没有孩子节点,便不再添加到队列中,下同理
40        if (leftNode != null) {
41            queue.add(leftNode);
42        }
43        // 添加右子节点
44        TreeNode rightNode = toNode(dataArr[++index]);
45        cur.right = rightNode;
46        if (rightNode != null) {
47            queue.add(rightNode);
48        }
49    }
50    return root;
51}
52
53private TreeNode toNode(String val) {
54    if (!"null".equals(val)) {
55        return new TreeNode(Integer.parseInt(val));
56    } else {
57        return null;
58    }
59}

Breadth-first search (BFS)

  • First, the root node into the queue.

  • Take the first node from the queue, and checks whether it is the goal.

  • If the target is found, the end of the search and return the results.

  • Otherwise it will be all direct child node has not been inspected to join the queue.

  • If the queue is empty, showing the entire FIG have checked - i.e. the target is not to be searched FIG. The end of the search and return "not find the target."

  • Repeat Step 2.

 1public List<List<Integer>> levelOrder(TreeNode root) {
 2    List<List<Integer>> res = new ArrayList<List<Integer>>();
 3    List<TreeNode> quene = new ArrayList<TreeNode>();
 4    if(root == null){
 5        return res;
 6    }
 7    quene.add(root);
 8    while(quene.size()!=0){
 9        int count = quene.size();
10        List<Integer> list = new ArrayList<Integer>();
11        while(count>0){
12            TreeNode temp =quene.remove(0);                   
13            list.add(temp.val);
14            if(temp.left!=null){
15                quene.add(temp.left);
16            }
17            if(temp.right!=null){
18                quene.add(temp.right);
19            } 
20            count--;
21        }
22        res.add(list);
23    }
24    return res;
25}

Morris traversal (Morris)

For when we usually binary tree is traversed using a recursive traversal or a stack-based to traverse, both of which have the worst is O (n) space complexity (recursive method would waste more time on recursive calls), and the time complexity of O (n) a. For the time complexity, since time required to traverse each element, the O (n) are the optimum conditions. So it can only be optimized for space. Morris traversing how to do it? First, we need to analyze and stack-based recursive traversal why they have O (n) space occupied. The following diagram simple binary tree traversal example:

For example, inorder traversal (LDR), starting at 1:

  • 2 has a left child, into the stack 1, to node 2;

  • 2 has left child. 4, placed on the stack 2, to node 4;

  • 4 left child is empty, the output node 4, the right child node 4 at this time is empty, popped back to the node 2;

  • An output node 2, the node 2 has a right child 5, to node 5;

  • 5 children left empty, the output node 5, this time right child node 5 is also empty, popped back to the node 1;

Can be learned from the above analysis, the use of a conventional traverse space to store the parent node does not implement all of the operations, such as for a node, a L starts operation, no D, R operation is required for storage. To solve this problem, Morris algorithm uses the concept of "clues binary tree", the use of null pointer around the leaf node points to some kind of node traversal order of the predecessor or a successor node. Morris inorder traversal algorithm process:

  • Current node 1 to the node set;

  • Current node is not empty, and there is left child, then find the right node in a left subtree of the node, i.e. the node 5, so that the right pointer points to the children themselves, i.e., link1;

  • Current node moves to the left pointer left child node 2, and deletes the parent node to point to null, i.e. remove erase1;

  • Node 2 is not empty, and have left children, then find 2 left sub-tree node rightmost node, that node 4, right child pointer to make it their own, namely link2;

  • Current node moves the pointer to the left child node 4 left, and delete the parent node to point to null, i.e. remove erase2;

  • 4 left child node is empty, the output node 4, node 2 moves to the right child;

  • No left child node 2 (pointer to null), the output node 2, to move to the right child node 5;

  • 5 no left child node, the output node 5, to move to the right child node 1;

  • No left child node 2 (pointer to null), the output nodes 1, 3 move to the right child node;

Code

 1void Morris_inorderTraversal(TreeNode root) {
 2    TreeNode curr = root;
 3    TreeNode pre;
 4    while (curr != null) {   
 5        if (curr.left == null) {    // 左孩子为空
 6            System.out.print(curr.val+" ");
 7            curr = curr.right; 
 8        }
 9        else {   // 左孩子不为空
10            // 找左子树中的最右节点
11            pre = curr.left; 
12            while (pre.right != null) {  
13                pre = pre.right;
14            }
15            // 删除左孩子,防止循环
16            pre.right = curr; 
17            TreeNode temp = curr; 
18            curr = curr.left; 
19            temp.left = null;
20        }
21    }
22}

AVL tree

AVL tree is a balanced binary tree, balanced binary tree recursively defined as follows:

  • Subtree height difference of about 1 or less.

  • Each of its sub-trees are balanced binary tree.

In order to ensure a balanced binary tree, AVL tree introduces a so-called monitoring mechanisms, that is, in a certain part imbalance tree triggers a corresponding balancing operation after more than one threshold value. Ensure the balance of the tree within the acceptable range. Since the introduction of a monitoring mechanism, we necessarily need a monitoring indicators, in order to determine the need for balancing operations. The monitoring indicators is called "balance factor (Balance Factor)". It is defined as follows:

  • Balancing factor: certain height left subtree of nodes right subtree minus the height of the resulting difference.

Based on balance factor, we can define AVL tree.

  • AVL tree: the absolute value of the balance factor of all the nodes does not exceed 1 binary.

In order to calculate the equilibrium factor, we need to introduce a highly natural property in this node. Here, we have a maximum height of about sub-tree node for its highly defined. Thus, the introduction of the node defines the height attribute AVL tree as follows:

1public class TreeNode {
2      int val;
3      int height;
4      TreeNode left;
5      TreeNode right;
6      TreeNode(int x) { val = x; }
7}

Nodes and different places above here is that we added an extra height to record the height of each node, and how to get the height of each node is very simple, speaking in front of the algorithm can be implemented in any kind of thinking, I not go into here, but here to say that it is, corresponding, we need to update the height of all the affected nodes during the following operations:

  • When the insertion node, along the insertion path height value node update

  • When deleting nodes (delete), along a path update deleted height value node

Then we redefine the node, with the height of the property, calculate the equilibrium factor of the operation is very simple to implement, that is, the balance factor of a node node height = left - right node height.

When the absolute value of the balance factor is greater than 1, the correction is triggered tree, or rebalancing operation.

Tree balancing operation

Balanced binary tree has two basic operations: left and right hand. L, i.e., counterclockwise rotation; right-handed, that is, clockwise. This rotation of the balance of the whole process may be one or more times, these two operations are out of balance from the smallest sub-tree root begins (i.e., from the insertion node and the balance factor recently over one progenitor node ). Wherein the right-handed operation diagram is as follows:

The so-called right-handed operation, that is, the Node B and the node C in the figure above the so-called "father exchange." In only three nodes time, it is very simple. But when there is a right child node B, things get a little complicated. We usually operation is: the right to abandon the child, to connect the node C and rotated, left to become the child node C. Thus, the corresponding code is as follows.

 1TreeNode treeRotateRight(TreeNode root) {
 2    TreeNode left = root.left;
 3
 4    root.left = left.right; // 将将要被抛弃的节点连接为旋转后的 root 的左孩子
 5    left.right = root; // 调换父子关系
 6
 7    left.height = Math.max(treeHeight(left.left), treeHeight(left.right))+1;
 8    right.height = Math.max(treeHeight(right.left), treeHeight(right.right))+1;
 9
10    return left;
11}

And a counterclockwise operation diagram is as follows:

Left and right-handed operation is very similar operations, the only difference is the need to swap the left and right lower. We can think of these two operations are symmetric. code show as below:

 1TreeNode treeRotateLeft(TreeNode root) {
 2    TreeNode right = root.ight;
 3
 4    root.right = right.left;
 5    right.left = root;
 6
 7    left.height = Math.max(treeHeight(left.left), treeHeight(left.right))+1;
 8    right->height = Math.max(treeHeight(right.left), treeHeight(right.right))+1;
 9
10    return right;
11}

The need to balance the four cases

  • LL 型

The so-called LL type is left on the chart that case, because that is the root node in the left subtree of left child of adding a new node, the root cause of the balance factor becomes +2, binary tree out of balance. In this case, once the right-handed node n.

  • Type RR

Type RR and LL circumstances completely symmetrical type. N nodes only need to conduct a left to fix.

  • LR type

LR is a new node is inserted into the right subtree of left child of n imbalance caused. Then we need is firstly i n once again left once right-handed.

  • RL type

RL is a new node is inserted into the imbalance of the right child of the left subtree n caused. Then we need is firstly i right-handed once again to conduct a n L.

Judge these four cases is very simple. We (the absolute value of the balance factor greater than 1) according disrupted balance balance factor tree node and its child nodes to determine the type of balance.

Balanced operation is as follows:

 1int treeGetBalanceFactor(TreeNode root) {
 2    if(root == NULL)
 3        return 0;
 4    else
 5        return x.left.height - x.right.height;
 6}
 7
 8TreeNode treeRebalance(TreeNode root) {
 9    int factor = treeGetBalanceFactor(root);
10    if(factor > 1 && treeGetBalanceFactor(root.left) > 0) // LL
11        return treeRotateRight(root);
12    else if(factor > 1 && treeGetBalanceFactor(root.left) <= 0) { //LR
13        root.left = treeRotateLeft(root.left);
14        return treeRotateRight(temp);
15    } else if(factor < -1 && treeGetBalanceFactor(root.right) <= 0) // RR
16        return treeRotateLeft(root);
17    else if((factor < -1 && treeGetBalanceFactor(root.right) > 0) { // RL
18        root.right = treeRotateRight(root.right);
19        return treeRotateLeft(root);
20    } else { // Nothing happened.
21        return root;
22    }
23}

It is recommended that a dynamic AVL tree website https://www.cs.usfca.edu/~galles/visualization/AVLtree.html, AVL can be understood by the dynamic visual way.

Original link:

https://blog.csdn.net/DBC_121/article/details/104584060

【End】

Recommended Reading 

Baidu established Internet hospital; nail recruit students to experience products division; iOS 13.4 on line | Geeks headlines

nearly 10 years programming language rookie big PK, Pick it!

Your Business Under what circumstances need to artificial intelligence? Take a look at what conditions and capabilities you need to have it!

5 bn bo suspected data leak, how to avoid stepping Python reptile sinkhole?

@ developers, Microsoft CEO Satya led the 60 large build-up to make coffee, you dare to take it?

claiming Nakamoto he was angry judge hate: your testimony there is no credibility!

You look at every point, I seriously as a favorite

Released 1878 original articles · won praise 40000 + · Views 17,070,000 +

Guess you like

Origin blog.csdn.net/csdnnews/article/details/105108967