Data structure - related operations for binary trees

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.

129 original articles were published. 37 were praised. 90,000 visits+
Private letter follow

Posted by pcw on Fri, 07 Feb 2020 22:48:12 -0800