[XJTUSE] data structure learning -- 3.4 binary search tree

Posted by Hoangsta on Sat, 05 Feb 2022 10:32:57 +0100

3.4 binary search tree

The function of binary search tree

one ️⃣ Provides the ability to find elements that take logn time

two ️⃣ Provides the ability to insert and delete elements taking logn time

For 10000 data

■ using a linear table to find elements requires an average of 5000 comparisons

■ using binary search tree to find elements only needs 14 times on average

Definition: if the value of any node of the binary search tree is k, the value of any node in the left subtree of the node is less than K; The value of any node in the right subtree of the node is greater than or equal to K.

Interface and node definition

For BST, its interface is defined as follows

public interface BSTADT <K extends Comparable<K>, V> {
    public void insert(K key, V value);//Insert Knot 
    public V remove(K key);//Delete nodes according to the key value
    public boolean update(BinNode<K, V> rt, K key, V value);//Update node value
    public V search(K key);//Search the value of the node corresponding to the key
    public int getHeight(BinNode<K, V> rt);//Gain tree height
    public boolean isEmpty();//Judge whether it is empty
    public void clear();//Empty tree
}

The nodes of the tree are defined as follows

public class BinNode<K extends Comparable<K>, V> {
    private K key;
    private V value;

    private BinNode<K, V> left;
    private BinNode<K, V> right;

    public BinNode(K key, V value){
        left = right = null;
        this.key=key;
        this.value=value;
    }

    public BinNode() {
    }

    public boolean isLeaf() {
        return left == null && right == null;
    }

    public K getKey() {
        return key;
    }

    public void setKey(K key) {
        this.key = key;
    }

    public V getValue() {
        return value;
    }

    public void setValue(V value) {
        this.value = value;
    }

    public BinNode<K, V> getLeft() {
        return left;
    }

    public void setLeft(BinNode<K, V> left) {
        this.left = left;
    }

    public BinNode<K, V> getRight() {
        return right;
    }

    public void setRight(BinNode<K, V> right) {
        this.right = right;
    }
}

Find an element

Algorithm for finding an element in binary search tree

one ️⃣ Set the current node to point to the root node

two ️⃣ Repeat the following steps;

■ if the current node is empty, exit and no element to match is found

■ if the keyword of the element contained in the current node is greater than the keyword of the element to be searched, set the current node to point to its left child node

■ if the keyword of the element contained in the current node is less than the keyword of the element to be searched, set the current node to point to its right child node

■ otherwise, the matching element is found and exits

public V search(K key) {
    return search(root, key);
}

private V search(BinNode<K, V> rt, K key) {
    try {
        if (key == null)
            throw new Exception("key is null");
        if (rt == null)
            return null;
        if (key.compareTo(rt.getKey()) < 0)
            return search(rt.getLeft(), key);//If it is less than the current key value, search the left subtree
        if (key.compareTo(rt.getKey()) > 0)
            return search(rt.getRight(), key);//If it is greater than the current key value, search the right subtree
        return rt.getValue();//Value found
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

Find and delete the smallest element

Finding the smallest element is relatively simple. You only need to recursively find it to the left until the left child node is empty

The idea of deleting the smallest element is as follows

private K getMinNode(BinNode<K, V> rt) {
    if (rt.getLeft() == null)
        return rt.getKey();
    else
        return getMinNode(rt.getLeft());
}
//Returns the updated root node
private BinNode<K, V> removeMinNode(BinNode<K, V> rt) {
    if (rt.getLeft() == null) {
        return rt.getRight();
    }//The right node of the last node. If it is empty, it returns NULL; otherwise, it returns the node
    rt.setLeft(removeMinNode(rt.getLeft()));//Continuous recursive update
    //It ensures the standardization of binary search tree
    return rt;
}

Insert an element

Find the inserted leaf node or branch node first

The inserted location should be the empty child node of the parent node of the location

public void insert(K key, V value) {
    try {
        if (key == null) {
            throw new Exception("Key is null, insert fault!");
        }
        if (value == null) {
            throw new Exception("Value is null, insert fault!.");
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    root = insertHelp(root, key, value);
} 

private BinNode<K, V> insertHelp(BinNode<K, V> rt, K key, V value) {
    if (rt == null) {
        return new BinNode<K, V>(key, value);
    }

    if (key.compareTo(rt.getKey()) < 0) {
        rt.setLeft(insertHelp(rt.getLeft(), key, value));
    }//It is similar to deleting nodes

    else if (key.compareTo(rt.getKey()) > 0) {
        rt.setRight(insertHelp(rt.getRight(), key, value));
    }//It is similar to deleting nodes

    else {
        rt.setValue(value);
    }
    return rt;
}

Balance

Balance factor: the height difference between the left and right subtrees

As long as the absolute value of the balance factor is less than or equal to 1, it means that the tree is balanced

For binary search tree, if the sequence of given elements is good, the balance is very poor

The destruction of the balance factor can be solved by rotation (which will be discussed in detail later)

Delete an element

If the deleted node has two nodes, it is special, and the structure of binary search tree should not be damaged

Using substitution, the deletion with two child nodes is replaced by the deletion with one child node

Replace the smallest node in the right subtree with the node to be deleted

/**
 *
 * @param key keyword
 * @return Delete value of node
 */
public V remove(K key) {
    removeValue = null;
    try {
        if (key == null)
            throw new Exception("Key is null, remove failure");
        root = removeHelp(root, key);
        return removeValue;
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

private BinNode<K, V> removeHelp(BinNode<K, V> rt, K key) {
    if (rt == null)
        return null;
    if (key.compareTo(rt.getKey()) < 0) {
        rt.setLeft(removeHelp(rt.getLeft(), key));
    } else if (key.compareTo(rt.getKey()) > 0) {
        rt.setRight(removeHelp(rt.getRight(), key));
    } else {
        if (rt.getLeft() == null) {
            removeValue = rt.getValue();
            rt = rt.getRight();
            //If the left child node is empty, the right child node is directly taken as the current root node
        } else if (rt.getRight() == null) {
            removeValue = rt.getValue();
            rt = rt.getLeft();
            //If the right child node is empty, the left child node is directly taken as the current root node
        } else {
            //The node to be deleted has two child nodes
            rt.setKey((K) getMinNode(rt.getRight()).getKey());
            rt.setValue((V) getMinNode(rt.getRight()).getValue());
            //Update the key and value of the current node to the value of the smallest node in the right subtree
            rt.setRight(removeMinNode(rt.getRight()));
            //Update the right child node of the current node
        }
    }
    return rt;
}

Each time cost

Search cost

The operation cost of balanced binary search tree is O(logn)

The worst cost of unbalanced binary search tree is O(n)

The costs of inserting and deleting are similar to those of searching

The cost of traveling around a binary search tree is O(n)

Only when a binary search tree is balanced can it really play its role

source code

/**
 * @author yjq
 * @version 1.0
 * @date 2021/11/20 22:51
 */

public class BinarySearchTree<K extends Comparable<K>, V> implements BSTADT<K, V> {
    private BinNode<K, V> root;
    private V removeValue;

    public BinarySearchTree() {
        root = null;
    }

    public BinNode<K, V> getRoot() {
        return root;
    }

    public void insert(K key, V value) {
        try {
            if (key == null) {
                throw new Exception("Key is null, insert fault!");
            }
            if (value == null) {
                throw new Exception("Value is null, insert fault!.");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        root = insertHelp(root, key, value);
    }

    private BinNode<K, V> insertHelp(BinNode<K, V> rt, K key, V value) {
        if (rt == null) {
            return new BinNode<K, V>(key, value);
        }

        if (key.compareTo(rt.getKey()) < 0) {
            rt.setLeft(insertHelp(rt.getLeft(), key, value));
        }//It is similar to deleting nodes

        else if (key.compareTo(rt.getKey()) > 0) {
            rt.setRight(insertHelp(rt.getRight(), key, value));
        }

        else {
            rt.setValue(value);
        }//Update if the key value is the same
        return rt;
    }

    /**
     *
     * @param key keyword
     * @return Value of deleted node
     */
    public V remove(K key) {
        removeValue = null;
        try {
            if (key == null)
                throw new Exception("Key is null, remove failure");
            root = removeHelp(root, key);
            return removeValue;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private BinNode<K, V> removeHelp(BinNode<K, V> rt, K key) {
        if (rt == null)
            return null;
        if (key.compareTo(rt.getKey()) < 0) {
            rt.setLeft(removeHelp(rt.getLeft(), key));
        } else if (key.compareTo(rt.getKey()) > 0) {
            rt.setRight(removeHelp(rt.getRight(), key));
        } else {
            if (rt.getLeft() == null) {
                removeValue = rt.getValue();
                rt = rt.getRight();
                //If the left child node is empty, the right child node is directly taken as the current root node
            } else if (rt.getRight() == null) {
                removeValue = rt.getValue();
                rt = rt.getLeft();
                //If the right child node is empty, the left child node is directly taken as the current root node
            } else {
                //The node to be deleted has two child nodes
                rt.setKey((K) getMinNode(rt.getRight()).getKey());
                rt.setValue((V) getMinNode(rt.getRight()).getValue());
                //Update the key and value of the current node to the value of the smallest node in the right subtree
                rt.setRight(removeMinNode(rt.getRight()));
                //Update the right child node of the current node
            }
        }
        return rt;
    }

    private BinNode getMinNode(BinNode<K, V> rt) {
        if (rt.getLeft() == null)
            return rt;
        else
            return getMinNode(rt.getLeft());
    }

    //Returns the updated root node
    private BinNode<K, V> removeMinNode(BinNode<K, V> rt) {
        if (rt.getLeft() == null) {
            return rt.getRight();
        }
        rt.setLeft(removeMinNode(rt.getLeft()));
        //It ensures the standardization of binary search tree
        return rt;
    }

    public V search(K key) {
        return search(root, key);
    }

    private V search(BinNode<K, V> rt, K key) {
        try {
            if (key == null)
                throw new Exception("key is null");
            if (rt == null)
                return null;
            if (key.compareTo(rt.getKey()) < 0)
                return search(rt.getLeft(), key);//If it is less than the current key value, search the left subtree
            if (key.compareTo(rt.getKey()) > 0)
                return search(rt.getRight(), key);//If it is greater than the current key value, search the right subtree
            return rt.getValue();//Value found
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public boolean update(K key, V value) {
        return update(root, key, value);
    }

    public boolean update(BinNode<K, V> rt, K key, V value) {
        try {
            if (key == null)
                throw new Exception("Key is null, update failure.");
            if (value == null)
                throw new Exception("value is null, update failure");
            if (key.compareTo(rt.getKey()) == 0) {
                rt.setValue(value);
                return true;
            }
            if (key.compareTo(rt.getKey()) < 0)
                return update(rt.getLeft(), key, value);
            if (key.compareTo(rt.getKey()) > 0)
                return update(rt.getRight(), key, value);
            return false;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    public boolean isEmpty() {
        return root == null;
    }

    public void clear() {
        root = null;
    }


    public int getHeight(BinNode<K, V> rt) {
        int height = 0;
        if (rt == null)
            return 0;
        else
            height++;
        height += Math.max(getHeight(rt.getLeft()), getHeight(rt.getRight()));
        return height;
    }

}

Topics: data structure BST