Teach you how to use java to realize binary search tree and its related operations

Posted by cauri on Fri, 14 Jan 2022 07:51:45 +0100

The basic operations of Binary Search Tree include search, maximum, minimum, previous, subsequent, insert and delete.

The time spent on the basic operation of the binary search tree is proportional to the height of the tree. For example, for a complete binary tree with n nodes, the time complexity of the basic operation on it is O(logn). However, if the tree is a linear chain with n nodes, the time complexity in this case is O(n).

1. What is a binary lookup tree

Binary search tree is an organized binary tree. We can represent such a tree by linking nodes. Each node contains five attributes: key, data, left, right, and parent.

If a node has no parent node or a child node, the value of its corresponding attribute is null.

Create a node Java file, which represents the node with the following code:

public class Node {
    public int key;
    public int data;
    public Node left;
    public Node right;
    public Node parent;
    
    public Node(){}
    
    public Node(int key) {
        this.key = key;
    }
}

Create a binarysearchtree Java file, representing the binary search tree with the following code:

public class BinarySearchTree {
    public Node root;
}

Next, the code will be supplemented step by step.

For keys stored in binary search tree, the following conditions must be met (characteristics of binary search tree):

For any node n in the binary tree, if left is any node in the left subtree of N, there is left key <= n.key; If right is any node in n right subtree, then n.key < = right key.

If we traverse the binary search tree in middle order, we can print all the key s in ascending order.

Available at binarysearchtree Add the following code to the java file to realize medium order traversal:

	private void innerWalk(Node node) {
        if(node != null) {
            innerWalk(node.left);
            System.out.print(node.key + " ");
            innerWalk(node.right);
        }
    }

    public void innerWalk() {
        this.innerWalk(this.root);
        System.out.println();
    }

2. Query binary search tree

2.1. Search

Search for the node with key in the binary search tree. If it is found, it returns the node; otherwise, it returns null. It can be found by using the characteristics of binary search tree.

Available at binarysearchtree Add the following code to the java file to realize the search:

	public Node search(int key) {
        Node pointer = this.root;
        while (pointer != null && pointer.key != key) {
            pointer = key < pointer.key ? pointer.left : pointer.right;
        }
        return pointer;
    }

2.2 minimum and maximum values

The node with the smallest key can be traversed and tracked from the root node to the last one according to the characteristics of the binary tree.

Available on node Add the following code to the java file:

    public Node minimum() {
        Node pointer = this;
        while(pointer.left != null){
            pointer = pointer.left;
        }
        return pointer;
    }

In binarysearchtree Add the following code to the java file:

 	public Node minimum() {
        return this.root.minimum();
    }

The node with the largest key can be traversed and tracked from the root node to the right child node according to the characteristics of the binary tree.

Available on node Add the following code to the java file:

    public Node maximum() {
        Node pointer = this;
        while(pointer.right != null) {
            pointer = pointer.right;
        }
        return pointer;
    }

In binarysearchtree Add the following code to the java file:

 	public Node minimum() {
        return this.root.maximum();
    }

2.3 precursors and successors

Given a node in a binary search tree, sometimes we need to find its direct successor (the first one behind) when all nodes in the tree are sorted in ascending order.

If all keys in the tree are different, the direct successor of node n is the smallest node greater than n.key.

If the node has a right node, its direct successor is the node with the smallest right subtree key. If the node is, it is judged whether the node is the left child node or the right child node of its parent node.

If it is a left child node, its parent node is returned. If it is a right child node, judge whether its parent node is the left child node or the right child node of its parent node, and so on.

null if not found.

Available on node Add the following implementation code to the java file:

    public Node successor() {
        Node pointer = this;
        if (pointer.right != null)
            return pointer.right.minimum();
        Node parentPointer = pointer.parent;
        while (parentPointer != null && parentPointer.right == pointer) {
            pointer = parentPointer;
            parentPointer = parentPointer.parent;
        }
        return pointer;
    }

The direct predecessor of a node refers to the first in front of the node when all nodes in the tree are sorted in ascending order. Seeking direct predecessor and seeking direct successor are symmetrical.

Available on node Add the following implementation code to the java file:

    public Node predecessor() {
        Node pointer = this;
        if (pointer.left != null)
            return pointer.left.maximum();
        Node parentPointer = pointer.parent;
        while (parentPointer != null && parentPointer.left == pointer) {
            pointer = parentPointer;
            parentPointer = parentPointer.parent;
        }
        return pointer;
    }

3. Insert and delete

Insertion and deletion will change the binary search tree, but its characteristics must be maintained.

3.1 insertion

Find a key in the tree that is closest to the node where you want to insert the node key, and there is a location where you can insert it, and then insert it in the appropriate location.

Available at binarysearchtree Add the following implementation code to the java file:

    public void insert(Node node) {
        Node pointer = this.root;
        Node parentPointer = null;
        while (pointer != null) {
            parentPointer = pointer;
            pointer = node.key < pointer.key ? pointer.left : pointer.right;
        }
        node.parent = parentPointer;
        if (parentPointer == null)
            this.root = node;
        else if (node.key < parentPointer.key) {
            parentPointer.left = node;
        } else {
            parentPointer.right = node;
        }
    }

3.2. Delete

After deleting nodes, we should maintain the characteristics of binary search tree.

If you want to delete a node without any child nodes, you can delete it directly without any processing.

If the node to be deleted has only one left subtree, the root node of the left subtree can be used to replace the node to be deleted, or the node with the largest key in the left subtree can be used to replace the node to be deleted.

If the node to be deleted has only one right subtree, the root node of the right subtree can be used to replace the node to be deleted, or the node with the smallest key in the right subtree can be used to replace the node to be deleted.

If the node to be deleted has both a left subtree and a right subtree, the node with the largest key in the left subtree can be used to replace the node to be deleted, or the node with the smallest key in the right subtree can be used to replace the node to be deleted.

Suppose you want to delete a node from the binary lookup tree, the specific steps of a deletion method are as follows:

  1. If the node has no left child node, then we can use the right child node of the node Right point instead of node, node Right may or may not be null.
  2. If node has only left child node Left, then we use node Left replaces node.
  3. If node has left child node Left and right child node right. We need to find the direct successor s of node (the node with the smallest key in the right subtree of node, which has no left child node), s in the right subtree of node, and then replace node with S. The following two situations should be considered:
    1. If s is the right child node of node, s can be used instead of node.
    2. Otherwise, replace s with the right child node of S, and then replace node with S.

Because we need to replace the subtree in the binary search tree, we can write a method to replace the subtree first.

Available at binarysearchtree Add the following implementation code to the java file:

    /**
     * Replacing node1 with node2
     * 
     * @param node1
     * @param node2
     */
    private void transplant(Node node1, Node node2) {
        if (node1.parent == null) {
            this.root = node2;
        } else if (node1.parent.left == node1) {
            node1.parent.left = node2;
        } else {
            node1.parent.right = node2;
        }

        if (node2 != null)
            node2.parent = node1.parent;
        node1.parent = null;
    }

Next, explain the code implementation of the delete node operation.

Available at binarysearchtree Add the following implementation code to the java file:

    public void delete(Node node) {
        if (node.left == null) {
            this.transplant(node, node.right);
        } else if (node.right == null) {
            this.transplant(node, node.left);
        } else {
            Node successor = node.successor();
            if (successor.parent != node) {
                this.transplant(successor, successor.right);
                successor.right = node.right;
                successor.right.parent = successor;
            }
            this.transplant(node, successor);
            successor.left = node.left;
            successor.left.parent = successor;
        }
    }

4. Complete code

Node.java file

public class Node {
    public int key;
    public int data;
    public Node left;
    public Node right;
    public Node parent;

    public Node() {
    }

    public Node(int key) {
        this.key = key;
    }

    public Node minimum() {
        Node pointer = this;
        while (pointer.left != null)
            pointer = pointer.left;
        return pointer;
    }

    public Node maximum() {
        Node pointer = this;
        while (pointer.right != null)
            pointer = pointer.right;
        return pointer;
    }

    public Node successor() {
        Node pointer = this;
        if (pointer.right != null)
            return pointer.right.minimum();
        Node parentPointer = pointer.parent;
        while (parentPointer != null && parentPointer.right == pointer) {
            pointer = parentPointer;
            parentPointer = parentPointer.parent;
        }
        return pointer;
    }

    public Node predecessor() {
        Node pointer = this;
        if (pointer.left != null)
            return pointer.left.maximum();
        Node parentPointer = pointer.parent;
        while (parentPointer != null && parentPointer.left == pointer) {
            pointer = parentPointer;
            parentPointer = parentPointer.parent;
        }
        return pointer;
    }
}

BinarySearchTree.java file

public class BinarySearchTree {
    public Node root;

    private void innerWalk(Node node) {
        if (node != null) {
            innerWalk(node.left);
            System.out.print(node.key + " ");
            innerWalk(node.right);
        }
    }

    public void innerWalk() {
        this.innerWalk(this.root);
        System.out.println();
    }

    public Node search(int key) {
        Node pointer = this.root;
        while (pointer != null && pointer.key != key) {
            pointer = key < pointer.key ? pointer.left : pointer.right;
        }
        return pointer;
    }

    public Node minimum() {
        return this.root.minimum();
    }

    public Node maximum() {
        return this.root.maximum();
    }

    public void insert(Node node) {
        Node pointer = this.root;
        Node parentPointer = null;
        while (pointer != null) {
            parentPointer = pointer;
            pointer = node.key < pointer.key ? pointer.left : pointer.right;
        }
        node.parent = parentPointer;
        if (parentPointer == null)
            this.root = node;
        else if (node.key < parentPointer.key) {
            parentPointer.left = node;
        } else {
            parentPointer.right = node;
        }
    }

    /**
     * Replacing node1 with node2
     * 
     * @param node1
     * @param node2
     */
    private void transplant(Node node1, Node node2) {
        if (node1.parent == null) {
            this.root = node2;
        } else if (node1.parent.left == node1) {
            node1.parent.left = node2;
        } else {
            node1.parent.right = node2;
        }

        if (node2 != null)
            node2.parent = node1.parent;
        node1.parent = null;
    }

    public void delete(Node node) {
        if (node.left == null) {
            this.transplant(node, node.right);
        } else if (node.right == null) {
            this.transplant(node, node.left);
        } else {
            Node successor = node.successor();
            if (successor.parent != node) {
                this.transplant(successor, successor.right);
                successor.right = node.right;
                successor.right.parent = successor;
            }
            this.transplant(node, successor);
            successor.left = node.left;
            successor.left.parent = successor;
        }
    }
}

5. Demonstration

Demo code:

public class Test01 {
    public static void main(String[] args) {
        Node n1 = new Node(1);
        Node n2 = new Node(2);
        Node n3 = new Node(3);
        Node n4 = new Node(4);
        Node n5 = new Node(5);

        BinarySearchTree bst = new BinarySearchTree();
        bst.insert(n3);
        bst.insert(n4);
        bst.insert(n2);
        bst.insert(n1);
        bst.insert(n5);

        System.out.println("bst.minimum().key: " + bst.minimum().key);
        System.out.println("bst.maximum().key: " + bst.maximum().key);
        System.out.println("n3.successor().key: " + n3.successor().key);
        System.out.println("n3.predecessor().key: " + n3.predecessor().key);
        System.out.println("bst.search(4).key: " + bst.search(4).key);

        System.out.print("tree: ");
        bst.innerWalk();
        System.out.print("delete n3: ");
        bst.delete(n3);
        bst.innerWalk();
        System.out.print("delete n2: ");
        bst.delete(n2);
        bst.innerWalk();
        System.out.print("delete n1: ");
        bst.delete(n1);
        bst.innerWalk();
        System.out.print("delete n4: ");
        bst.delete(n4);
        bst.innerWalk();
        System.out.print("delete n5: ");
        bst.delete(n5);
        bst.innerWalk();
    }
}

result:

bst.minimum().key: 1
bst.maximum().key: 5
n3.successor().key: 4
n3.predecessor().key: 2
bst.search(4).key: 4
tree: 1 2 3 4 5 
delete n3: 1 2 4 5 
delete n2: 1 4 5 
delete n1: 4 5 
delete n4: 5 
delete n5: 

Topics: Java Algorithm data structure Binary tree