Tencent Daniel teaches you how to use Java to add, delete, retrieve and traverse binary trees.

An explanation of binary tree from Baidu Encyclopedia:

In computer science, a binary tree is a tree structure with at most two subtrees per node. Usually, subtrees are called "left subtree" and "right subtree". Binary trees are often used to implement binary search trees and binary heaps.

A binary tree with a depth of K and 2^k-1 nodes is called a full binary tree. The characteristic of this tree is that the number of nodes on each layer is the maximum number of nodes. In a binary tree, except for the last layer, if the other layers are full, and the last layer is either full, or there are several consecutive nodes on the right, then the binary tree is a complete binary tree. The depth of a complete binary tree with n nodes is floor(log2n)+1. A complete binary tree with a depth of K has at least 2k-1 leaf nodes and at most 2k-1 nodes.

The structure of binary tree:

Declarations of binary tree nodes:

static final class Entry<T extends Comparable<T>>{
        //Preserved data
        private T item;
        //Left tree
        private Entry<T> left;
        //Right subtree
        private Entry<T> right;
        //Parent node
        private Entry<T> parent;
        Entry(T item,Entry<T> parent){
            this.item = item;
            this.parent = parent;
        }
    }

Class properties:

 //root node
    private Entry<T> root;
    //Data volume
    private int size = 0;

Binary tree adds data:

/**
     * Additive elements
     * @param item Elements to be added
     * @return Added elements
     */
    public T put(T item){
        //Each time data is added, it traverses downward from the root node
        Entry<T> t = root;
  if (t == null){
            //The current fork tree is empty, and the new node is set to the root node and the parent node is null.
            root = new Entry<>(item,null);
       size++; 
            return root.item;
        }
        //Natural sorting results, if the incoming data is less than the current node returns - 1, greater than the current node returns 1, otherwise returns 0
        int ret = 0;
        //Record parent node
        Entry<T> p = t;
        while (t != null){
            //Comparing with the current node
            ret = item.compareTo(t.item);
            p = t;
            //The insertion node is smaller than the current node. Set the current node to the left sub-node, and then compare it with the left sub-node to find the appropriate location by analogy.
            if (ret < 0)
                t = t.left;
            //Greater than the current node
            else if (ret > 0)
                t = t.right;
            else {
                //Cover up old values when they are equal
                t.item = item;
                return t.item;
            }
        }
        //Create a new node
        Entry<T> e = new Entry<>(item,p);
        //Put the new node in the right place according to the comparison result
        if (ret < 0)
            p.left = e;
        else
            p.right = e;
     size++;
        return e.item;
    }

In the process of put ting, Comparable < T > compareTo is used to compare the size of two elements, so the elements stored in the binary tree must inherit the Comparable < T > class and override the compareTo method.

Binary Tree Delete Data

/**
     * Delete elements
     * If the deletion element is subdivided, it can be divided into four parts: no child node, only left node, only right node, and two child nodes.
     * 1)It's easy to do without child nodes, just delete them directly.
     * 2)Only the left node or the right node, the two cases are handled in the same way, but in the opposite direction, so together,
     * The left node (right node) of the parent node of the deleted node is pointed to the child node of the deleted node, and the left node (right node) is pointed to the parent node of the deleted node.
     * 3)There are two sub-nodes, which is relatively complex:
     * Find the precursor or or successor node of the deleted node, assign the value of the precursor or successor node to the deleted node, and then delete the precursor or successor node. The essence is to delete precursor or or successor nodes
     * The characteristics of the precursor node:
     * 1)If the deleted left child node has no right child node, then the left child node is the precursor node.
     * 2)If the left child of the deleted node has the right child, the last node on the right is the precursor node.
     * The characteristics of successor nodes are as follows:
     * Contrary to the precursor node, it is always the right child node, or the left-most child node of the right child node; for example, if the deletion node is c, the precursor node is m, and the successor node is n.
     *                                          a
     *                                       /     \
     *                                    b          c
     *                                  / \         /  \
     *                                d    e       f    g
     *                              /  \  / \     / \   / \
     * @param item Delete element h I J K L M n o
     * @return Delete result
     */
    public boolean remove(T item){
        //Get the deleted node
        Entry<T> delEntry = getEntry(item);
        if (delEntry == null) return false;
        //Delete the parent node of the node
        Entry<T> p = delEntry.parent;
        size--;
        //Case 1: No child nodes
        if (delEntry.left == null && delEntry.right == null){
            //Delete node as root node
            if (delEntry == root){
                root = null;
            }else {//Non root node
                //The left node of the parent node is deleted
                if (delEntry == p.left){
                    p.left = null;
                }else {//Delete the right node
                    p.right = null;
                }
            }
            //Case 2: Only the left node is deleted
        }else if (delEntry.right == null){
            Entry<T> lc = delEntry.left;
            //The deleted node is the root node, and the left node of the deleted node is set to the root node.
            if (p == null) {
                lc.parent = null;
                root = lc;
            } else {//Non root node
                if (delEntry == p.left){//Delete the left node
                    p.left = lc;
                }else {//Delete the right node
                    p.right = lc;
                }
                lc.parent = p;
            }
            //Case 3: Only the right node is deleted
        }else if (delEntry.left == null){
            Entry<T> rc = delEntry.right;
            //Delete the root node
            if (p == null) {
                rc.parent = null;
                root = rc;
            }else {//Delete non-root nodes
                if (delEntry == p.left)
                    p.left = rc;
                else
                    p.right = rc;
                rc.parent = p;
            }
            //Scenario 4: Delete a node with two child nodes
        }else {//There are two nodes, find the successor node, assign the value to the deletion node, and then delete the successor node.
            Entry<T> successor = successor(delEntry);//Get successor nodes
            delEntry.item = successor.item;
            //The successor node is the right child node
            if (delEntry.right == successor){
                //Right child node has right child node
                if (successor.right != null) {
                    delEntry.right = successor.right;
                    successor.right.parent = delEntry;
                }else {//Right child node has no child node
                    delEntry.right = null;
                }
            }else {//Successor node must be left node
                successor.parent.left = null;
            }
            return true;
        }
        //Let gc recycle
        delEntry.parent = null;
        delEntry.left = null;
        delEntry.right = null;
        return true;
    }

/**
     * Getting Node Nodes
     * @param item Get node
     * @return The acquired node, possibly null
     */
    private Entry<T> getEntry(T item){
        Entry<T> t = root;
        int ret;
        //Traverse from the root node
        for (;t != null;){
            ret = item.compareTo(t.item);
            if (ret < 0)
                t = t.left;
            else if (ret > 0)
                t = t.right;
            else
                return t;
        }
        return null;
    }

    /**
     * Finding successor nodes
     * @param delEntry Delete node
     * @return Successor node
     */
    private Entry<T> successor(Entry<T> delEntry) {
        Entry<T> r = delEntry.right;//r node must not be empty
        while (r.left != null){
            r = r.left;
        }
        return r;
    }

Binary tree acquisition node

    /**
     * Determine whether the element exists
     * @param item Search element
     * @return Result
     */
    public boolean contains(T item){
        return getEntry(item) != null;
    }

    /**
     * Getting Node Nodes
     * @param item Get node
     * @return The acquired node, possibly null
     */
    private Entry<T> getEntry(T item){
        Entry<T> t = root;
        int ret;
        //Traverse from the root node
        for (;t != null;){
            ret = item.compareTo(t.item);
            if (ret < 0)
                t = t.left;
            else if (ret > 0)
                t = t.right;
            else
                return t;
        }
        return null;
    }

Because my example is to store an element, and the element obtained is consistent with the element passed in, so I use contains method to judge that returning true means that the acquisition is successful, not returning the result obtained. Of course, if you change the elements stored in entry to kv, you can use the get method.

Traversal of Binary Trees

The traversal of binary tree can be divided into three kinds: pre-order traversal, middle-order traversal and follow-up traversal. The middle-order traversal is the most commonly used traversal method, because it traverses the ascending order of the results.

Preorder traversal:

     /**
     * Preorder traversal
     * @param e Start traversing elements
     */
    public void prevIterator(Entry<T> e){
        if (e != null) {
            System.out.print(e.item + " ");
            prevIterator(e.left);
            prevIterator(e.right);
        }
    }

Intermediate traversal:

   /**
     * Sequential traversal
     * @param e
     */
    public void midIterator(Entry<T> e){
        if (e != null){
            midIterator(e.left);
            System.out.print(e.item + " ");
            midIterator(e.right);
        }
    }

Post-order traversal:

    /**
     * Subsequent traversal
     * @param e Start traversing elements
     */
    public void subIterator(Entry<T> e){
        if (e != null) {
            subIterator(e.left);
            subIterator(e.right);
            System.out.print(e.item + " ");
        }
    }
package com.rainple.collections;

/**
 * Two fork tree
 * @param <T>
 */
public class BinaryTree<T extends Comparable<T>> {

    //root node
    private Entry<T> root;
    //Data volume
    private int size = 0;

    public BinaryTree(){}

    /**
     * Additive elements
     * @param item Elements to be added
     * @return Added elements
     */
    public T put(T item){
        //Each time data is added, it traverses downward from the root node
        Entry<T> t = root;
        size++;
        if (t == null){
            //The current fork tree is empty, and the new node is set to the root node and the parent node is null.
            root = new Entry<>(item,null);
            return root.item;
        }
        //Natural sorting results, if the incoming data is less than the current node returns - 1, greater than the current node returns 1, otherwise returns 0
        int ret = 0;
        //Record parent node
        Entry<T> p = t;
        while (t != null){
            //Comparing with the current node
            ret = item.compareTo(t.item);
            p = t;
            //The insertion node is smaller than the current node. Set the current node to the left sub-node, and then compare it with the left sub-node to find the appropriate location by analogy.
            if (ret < 0)
                t = t.left;
            //Greater than the current node
            else if (ret > 0)
                t = t.right;
            else {
                //Cover up old values when they are equal
                t.item = item;
                return t.item;
            }
        }
        //Create a new node
        Entry<T> e = new Entry<>(item,p);
        //Put the new node in the right place according to the comparison result
        if (ret < 0)
            p.left = e;
        else
            p.right = e;
        return e.item;
    }

    public void print(){
        midIterator(root);
    }

    /**
     * Sequential traversal
     * @param e
     */
    public void midIterator(Entry<T> e){
        if (e != null){
            midIterator(e.left);
            System.out.print(e.item + " ");
            midIterator(e.right);
        }
    }

    /**
     * Get the root node
     * @return root node
     */
    public Entry<T> getRoot(){return root;}

    /**
     * Preorder traversal
     * @param e Start traversing elements
     */
    public void prevIterator(Entry<T> e){
        if (e != null) {
            System.out.print(e.item + " ");
            prevIterator(e.left);
            prevIterator(e.right);
        }
    }

    /**
     * Subsequent traversal
     * @param e Start traversing elements
     */
    public void subIterator(Entry<T> e){
        if (e != null) {
            subIterator(e.left);
            subIterator(e.right);
            System.out.print(e.item + " ");
        }
    }

    /**
     * Getting Node Nodes
     * @param item Get node
     * @return The acquired node, possibly null
     */
    private Entry<T> getEntry(T item){
        Entry<T> t = root;
        int ret;
        //Traverse from the root node
        for (;t != null;){
            ret = item.compareTo(t.item);
            if (ret < 0)
                t = t.left;
            else if (ret > 0)
                t = t.right;
            else
                return t;
        }
        return null;
    }

    /**
     * Determine whether the element exists
     * @param item Search element
     * @return Result
     */
    public boolean contains(T item){
        return getEntry(item) != null;
    }

    /**
     * Delete elements
     * If the deletion element is subdivided, it can be divided into four parts: no child node, only left node, only right node, and two child nodes.
     * 1)It's easy to do without child nodes, just delete them directly.
     * 2)Only the left node or the right node, the two cases are handled in the same way, but in the opposite direction, so together,
     * The left node (right node) of the parent node of the deleted node is pointed to the child node of the deleted node, and the left node (right node) is pointed to the parent node of the deleted node.
     * 3)There are two sub-nodes, which is relatively complex:
     * Find the precursor or or successor node of the deleted node, assign the value of the precursor or successor node to the deleted node, and then delete the precursor or successor node. The essence is to delete precursor or or successor nodes
     * The characteristics of the precursor node:
     * 1)If the deleted left child node has no right child node, then the left child node is the precursor node.
     * 2)If the left child of the deleted node has the right child, the last node on the right is the precursor node.
     * The characteristics of successor nodes are as follows:
     * Contrary to the precursor node, it is always the right child node, or the left-most child node of the right child node; for example, if the deletion node is c, the precursor node is m, and the successor node is n.
     *                                          a
     *                                       /     \
     *                                    b          c
     *                                  / \         /  \
     *                                d    e       f    g
     *                              /  \  / \     / \   / \
     * @param item Delete element h I J K L M n o
     * @return Delete result
     */
    public boolean remove(T item){
        //Get the deleted node
        Entry<T> delEntry = getEntry(item);
        if (delEntry == null) return false;
        //Delete the parent node of the node
        Entry<T> p = delEntry.parent;
        size--;
        //Case 1: No child nodes
        if (delEntry.left == null && delEntry.right == null){
            //Delete node as root node
            if (delEntry == root){
                root = null;
            }else {//Non root node
                //The left node of the parent node is deleted
                if (delEntry == p.left){
                    p.left = null;
                }else {//Delete the right node
                    p.right = null;
                }
            }
            //Case 2: Only the left node is deleted
        }else if (delEntry.right == null){
            Entry<T> lc = delEntry.left;
            //The deleted node is the root node, and the left node of the deleted node is set to the root node.
            if (p == null) {
                lc.parent = null;
                root = lc;
            } else {//Non root node
                if (delEntry == p.left){//Delete the left node
                    p.left = lc;
                }else {//Delete the right node
                    p.right = lc;
                }
                lc.parent = p;
            }
            //Case 3: Only the right node is deleted
        }else if (delEntry.left == null){
            Entry<T> rc = delEntry.right;
            //Delete the root node
            if (p == null) {
                rc.parent = null;
                root = rc;
            }else {//Delete non-root nodes
                if (delEntry == p.left)
                    p.left = rc;
                else
                    p.right = rc;
                rc.parent = p;
            }
            //Scenario 4: Delete a node with two child nodes
        }else {//There are two nodes, find the successor node, assign the value to the deletion node, and then delete the successor node.
            Entry<T> successor = successor(delEntry);//Get successor nodes
            delEntry.item = successor.item;
            //The successor node is the right child node
            if (delEntry.right == successor){
                //Right child node has right child node
                if (successor.right != null) {
                    delEntry.right = successor.right;
                    successor.right.parent = delEntry;
                }else {//Right child node has no child node
                    delEntry.right = null;
                }
            }else {//Successor node must be left node
                successor.parent.left = null;
            }
            return true;
        }
        //Let gc recycle
        delEntry.parent = null;
        delEntry.left = null;
        delEntry.right = null;
        return true;
    }

    /**
     * Finding successor nodes
     * @param delEntry Delete node
     * @return Successor node
     */
    private Entry<T> successor(Entry<T> delEntry) {
        Entry<T> r = delEntry.right;//r node must not be empty
        while (r.left != null){
            r = r.left;
        }
        return r;
    }

    public int size(){return size;}

    public boolean isEmpty(){return size == 0;}

    public void clear(){
        clear(getRoot());
        root = null;
    }

    private void clear(Entry<T> e){
        if (e != null){
            clear(e.left);
            e.left = null;
            clear(e.right);
            e.right = null;
        }
    }

    static final class Entry<T extends Comparable<T>>{
        //Preserved data
        private T item;
        //Left tree
        private Entry<T> left;
        //Right subtree
        private Entry<T> right;
        //Parent node
        private Entry<T> parent;
        Entry(T item,Entry<T> parent){
            this.item = item;
            this.parent = parent;
        }
    }

}

Test code example:

public static void main(String[] args) {
       BinaryTree<Integer> binaryTree = new BinaryTree<>();
        //Release data
        binaryTree.put(73);
        binaryTree.put(22);
        binaryTree.put(532);
        binaryTree.put(62);
        binaryTree.put(72);
        binaryTree.put(243);
        binaryTree.put(42);
        binaryTree.put(3);
        binaryTree.put(12);
        binaryTree.put(52);

        System.out.println("size:  " + binaryTree.size());
        binaryTree.put(52);
        System.out.println("After adding the same element size:  " + binaryTree.size());
        //Judging whether data exists
        System.out.println("Does the data exist?" + binaryTree.contains(12));
        //Sequential traversal
        System.out.print("Intermediate order traversal results: ");
        binaryTree.midIterator(binaryTree.getRoot());
        System.out.println();
        //Preorder traversal
        System.out.print("Previous traversal results: ");
        binaryTree.prevIterator(binaryTree.getRoot());
        System.out.println();
        //Post order traversal
        System.out.print("Follow-up traversal results: ");
        binaryTree.subIterator(binaryTree.getRoot());
        //Delete data
        System.out.println();
        binaryTree.remove(62);
        System.out.println("After deleting the data, determine whether it exists:" + binaryTree.contains(62));
        //Empty Binary Tree
        binaryTree.clear();
        System.out.print("Intermediate traversal after clearing data: ");
        binaryTree.midIterator(binaryTree.getRoot());
    }

Test results:

size:  10
 After adding the same element, size:10
 Does the data exist:
Intermediate order traversal results: 3 12 22 42 52 62 72 73 243 532 
Previous ergodic results: 73 22 3 12 62 42 52 72 532 243 
Follow-up traversal results: 123 52 42 72 62 22 243 532 73 
After deleting the data, determine whether it exists:false
 Intermediate traversal after clearing data: 

Pure handwritten demo, welcome to correct any mistakes, thank you for reading!!!

Keywords: Java less

Added by legomez2000 on Fri, 17 May 2019 21:10:04 +0300