Weekly leetcode - realize the basic functions of binary search tree

Posted by blackhawk08 on Wed, 17 Nov 2021 05:37:54 +0100

Data structure and algorithm website recommendation:

http://520it.com/binarytrees/
http://btv.melezinek.cz/binary-search-tree.html
https://www.cs.usfca.edu/~galles/visualization/Algorithms.html
https://yangez.github.io/btree-js
https://www.codelike.in

1. Add node to binary search tree: add(E element)

public class BinarySearchTree<E extends Comparable<E>> {

    // Number of elements
    private int size;

    // Root node
    private Node<E> root;

    /**
     * Adding nodes to a binary search tree
     * 1,Find the parent node (under which node to add)
     * 2,Add current node
     * @param element
     */
    public void add(E element){
        // Parameter verification
        elementNotNullCheck(element);

        if(root==null){
            root = new Node<E>(element,null);
            size++;
            return;
        }
        // Parent node found
        // Record the root node found
        Node<E> parent = root;
        Node<E> node = root;
        // Record whether the node should be added to the left or right of the root node
        int compare = 0;
        while (node!=null){
            parent = node;
            compare = compare(element,node.element);
            // compare(o1,o2). If 1 is returned, O1 > O2, return - 1, O1 < O2, return 0, o1=o2
            if(compare>0){
                node = node.right;
            }else if(compare<0){
                node = node.lefe;
            }else{
                node.element = element;
                return;
            }
        }

        // After finding the parent node, see where to insert it into the parent node
        if(compare>0){
            parent.right = new Node<>(element,parent);
        }else{
            parent.lefe = new Node<>(element,parent);
        }
        size++;
    }

    private int compare(E element1,E element2) {
        return 0;
    }

    /**
     * Parameter verification
     * @param element
     */
    public void elementNotNullCheck(E element){
        if(element==null){
            throw new IllegalArgumentException("element must not null");
        }
    }

    /**
     * Define node classes
     * @param <E>
     */
    private static class Node<E>{
        private E element;
        private Node<E> lefe;
        private Node<E> right;
        private Node<E> parent;

        public Node(E element, Node<E> parent) {
            this.element = element;
            this.parent = parent;
        }
    }
}

2. Judge the adding direction when adding nodes: compare(E e1,E e2)

Nodes may not be directly comparable. For example, there is a Person class. The comparison method is through the age in the Person class

2.1 Comparable interface

① Write an interface Comparable to compare:

public interface Comparable<E> {
    public int compareTo(E e);
}

② Let the current class implement the Comparable interface and customize the comparison rules

public class Person implements Comparable<Person> {
    private int age;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Person() {
    }

    public Person(int age) {
        this.age = age;
    }

    @Override
    public int compareTo(Person o) {
        // If 1 is returned, this. Age > person. Age
        return this.age-o.age;
    }
}

③ Here, the default E has implemented the Comparable interface, so the element has the comparison function

public class BinarySearchTree<E extends Comparable>> {
    /**
     * Compare the size of two node values
     * @param element1
     * @param element2
     * @return
     */
    private int compare(E element1,E element2) {
        //Return to 1, indicating element1 > Element2
        return element1.compareTo(element2);
    }
}

④ Test:

public class Main {
    public static void main(String[] args) {
        BinarySearchTree<Person> binarySearchTree = new BinarySearchTree<Person>();
        binarySearchTree.add(new Person(12));
        binarySearchTree.add(new Person(15));
    }
}

2.2 Comparator interface

① Define comparator interface:

public interface Comparator<E> {
    int compare(E e1,E e2);
}

② Specify a comparator when creating a tree:

public class BinarySearchTree<E> {
    // comparator
    private Comparator<E> comparator;

    public BinarySearchTree(Comparator<E> comparator){
        this.comparator = comparator;
    }

    /**
     * Compare the size of two node values
     */
    private int compare(E element1,E element2) {
        //Return to 1, indicating element1 > Element2
        return comparator.compare(element1,element2);
    }
}

③ Person class:

public class Person {
    private int age;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Person() {
    }

    public Person(int age) {
        this.age = age;
    }
}

④ Test: the comparator is defined externally and uses an external class

public class Main {
    // External class: comparator
    private static class MyComparator1 implements Comparator<Person>{
        @Override
        public int compare(Person e1, Person e2) {
            return e1.getAge()-e2.getAge();
        }
    }
    private static class MyComparator2 implements Comparator<Person>{
        @Override
        public int compare(Person e1, Person e2) {
            return e2.getAge()-e1.getAge();
        }
    }

    public static void main(String[] args) {
        // The advantage of using the Comparator interface is that the comparison logic can be customized: different comparators are passed in
        // Using the Comparable interface requires following the comparison logic implemented in the Person class
        BinarySearchTree<Person> binarySearchTree = new BinarySearchTree<Person>(new MyComparator1());
        binarySearchTree.add(new Person(12));
        binarySearchTree.add(new Person(15));

        BinarySearchTree<Person> binarySearchTree2 = new BinarySearchTree<Person>(new MyComparator2());
        binarySearchTree.add(new Person(12));
        binarySearchTree.add(new Person(15));
    }
}

You can also define a comparator using an internal class:

public class Main {
    // Internal class: comparator
    public static void main(String[] args) {
        BinarySearchTree<Person> binarySearchTree = new BinarySearchTree<Person>(new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                return o1.getAge()-o2.getAge();
            }
        });
        binarySearchTree.add(new Person(12));
        binarySearchTree.add(new Person(15));

        BinarySearchTree<Person> binarySearchTree2 = new BinarySearchTree<Person>(new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                return o2.getAge()-o1.getAge();
            }
        });
        binarySearchTree2.add(new Person(12));
        binarySearchTree2.add(new Person(15));
    }
}

2.3 combination of comparable and Comparator interfaces

Comparator interface is a more flexible comparator, which is generally used in projects, and the two comparators can also be combined more perfectly.

If the Comparator is not passed in, it means that the class (Person) is Comparable, that is, it has implemented the Comparable interface. In fact, the java class library has implemented the Comparator and the Comparable interface, and many Comparable classes have also implemented the Comparable interface with the comparison function, such as:

public final class Integer implements Comparable<Integer{}
public final class String implements Comparable<String> {}

① Two comparators are used:

public class BinarySearchTree<E> {
    // comparator
    private Comparator<E> comparator;

    public BinarySearchTree(){
    }

    public BinarySearchTree(Comparator<E> comparator){
        this.comparator = comparator;
    }

    /**
     * Compare the size of two node values
     */
    private int compare(E element1,E element2) {
        if(comparator!=null){
            return comparator.compare(element1,element2);
        }
        // If no comparator is passed in, it means that the class E has implemented the Comparable interface. You can force it to (Comparable < E >) and call the comprareTo () method
        return ((Comparable<E>)element1).compareTo(element2);
    }
}

② If the Person class implements Comparable, you can construct BinarySearchTree without passing in the comparator

public class Person implements Comparable<Person> {
    private int age;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Person() {
    }

    public Person(int age) {
        this.age = age;
    }

    @Override
    public int compareTo(Person o) {
        // If 1 is returned, this. Age > person. Age
        return this.age-o.age;
    }
}

③ Test:

public class Main {
    // External class: comparator
    private static class MyComparator1 implements Comparator<Person> {
        @Override
        public int compare(Person e1, Person e2) {
            return e1.getAge()-e2.getAge();
        }
    }
    private static class MyComparator2 implements Comparator<Person>{
        @Override
        public int compare(Person e1, Person e2) {
            return e2.getAge()-e1.getAge();
        }
    }

    public static void main(String[] args) {
        BinarySearchTree<Person> binarySearchTree = new BinarySearchTree<Person>(new MyComparator1());
        binarySearchTree.add(new Person(12));
        binarySearchTree.add(new Person(15));

        BinarySearchTree<Person> binarySearchTree2 = new BinarySearchTree<Person>(new MyComparator2());
        binarySearchTree2.add(new Person(12));
        binarySearchTree2.add(new Person(15));

        // If no comparator is passed in, the Person class must implement the Comparable interface to customize the comparison rules
        BinarySearchTree<Person> binarySearchTree1 = new BinarySearchTree<>();
    }
}

3. Binary tree printer

① Copy the following code in the printer folder provided by the project into the project:

② The BinarySearchTree class implements the BinaryTreeInfo interface and overrides the four methods inside:

public class BinarySearchTree<E> implements BinaryTreeInfo {

    /**
     * The following four methods are used to print binary trees
     */
    @Override
    public Object root() {
        return root;
    }

    @Override
    public Object left(Object node) {
        return ((Node<E>)node).left;
    }

    @Override
    public Object right(Object node) {
        return ((Node<E>)node).right;
    }

    @Override
    public Object string(Object node) {
        return  ((Node<E>)node).element;
    }
}

③ Test printer:

public class Main {
    public static void main(String[] args) {
        test2();
    }

    static void test2() {
        Integer[] data = new Integer[] {7, 4, 9, 2, 5, 8, 11, 3};
        BinarySearchTree<Integer> bst = new BinarySearchTree<>();
        for (int i = 0; i < data.length; i++) {
            bst.add(data[i]);
        }
        //Print the binary tree and combine the printed binary tree with https://520it.com/binarytrees/ The binary search tree generated in is compared. If it is the same, it is right
        BinaryTrees.println(bst);
    }
}

Print results:

4. Traversal of binary tree

① Binary tree sequence traversal, pre order traversal, middle order traversal, post order traversal

public class BinarySearchTree<E> implements BinaryTreeInfo {

    // Number of elements
    private int size;

    // Root node
    private Node<E> root;

    // comparator
    private Comparator<E> comparator;

    public BinarySearchTree(){

    }

    public BinarySearchTree(Comparator<E> comparator){
        this.comparator = comparator;
    }

    /**
     * level traversal 
     */
    public void levelOrderTraversal(){
        if(root==null ) return;
        Queue<Node<E>> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()){
            Node<E> node = queue.poll();
            System.out.println(node.element);
            if(node.left!=null){
                queue.offer(node.left);
            }
            if(node.right!=null){
                queue.offer(node.right);
            }
        }
    }


    /**
     * Subsequent traversal
     */
    public void postorderTraversal(){
        postorderTraversal(root);
    }
    public void postorderTraversal(Node<E> node){
        if(node==null) return;
        postorderTraversal(node.left);
        postorderTraversal(node.right);
        System.out.println(node.element);
    }

    /**
     * Medium order traversal
     */
    public void inorderTraversal(){
        inorderTraversal(root);
    }
    public void inorderTraversal(Node<E> node){
        if(node==null) return;
        inorderTraversal(node.left);
        System.out.println(node.element);
        inorderTraversal(node.right);
    }

    /**
     * Preorder traversal
     */
    public void preorderTraversal(){
        preorderTraversal(root);
    }
    public void preorderTraversal(Node<E> node){
        if(node==null) return;
        System.out.println(node.element);
        preorderTraversal(node.left);
        preorderTraversal(node.right);
    }
}

② Test:

public class Main {
    public static void main(String[] args) {
        test();
    }

    public static void test(){
        BinarySearchTree<Integer> binarySearchTree = new BinarySearchTree<>();
        int[] arr = {7, 4, 9, 2, 5, 8, 11, 3, 12, 1};
        for(int i=0;i<arr.length;i++){
            binarySearchTree.add(arr[i]);
        }
        BinaryTrees.println(binarySearchTree);
        binarySearchTree.preorderTraversal();
        System.out.println();
        binarySearchTree.inorderTraversal();
        System.out.println();
        binarySearchTree.postorderTraversal();
        System.out.println();
        binarySearchTree.levelOrderTraversal();
    }
}

5. Design the interface of traversing binary tree

① In the original traversal method, after traversing the node, it is written directly, that is, the value of the node is printed directly. We can design an traversal interface and do some operations after traversing the node

public class BinarySearchTree<E> implements BinaryTreeInfo {

    // Number of elements
    private int size;

    // Root node
    private Node<E> root;

    // comparator
    private Comparator<E> comparator;

    public BinarySearchTree(){

    }

    public BinarySearchTree(Comparator<E> comparator){
        this.comparator = comparator;
    }


    /**
     * Traversal interface of binary tree
     */
    public static interface Visitor<E>{
        void visit(E element);
    }
    
    /**
     * level traversal 
     */
    public void levelOrderTraversal(Visitor<E> visitor){
        if(root==null ) return;
        Queue<Node<E>> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()){
            Node<E> node = queue.poll();
            visitor.visit(node.element);
            if(node.left!=null){
                queue.offer(node.left);
            }
            if(node.right!=null){
                queue.offer(node.right);
            }
        }
    }
    /**
     * Subsequent traversal
     */
    public void postorderTraversal(Visitor<E> visitor){
        postorderTraversal(root,visitor);
    }
    public void postorderTraversal(Node<E> node,Visitor<E> visitor){
        if(node==null || visitor==null) return;
        postorderTraversal(node.left,visitor);
        postorderTraversal(node.right,visitor);
        visitor.visit(node.element);
    }
    /**
     * Medium order traversal
     */
    public void inorderTraversal(Visitor<E> visitor){
        inorderTraversal(root,visitor);
    }
    public void inorderTraversal(Node<E> node,Visitor<E> visitor){
        if(node==null) return;
        inorderTraversal(node.left,visitor);
        visitor.visit(node.element);
        inorderTraversal(node.right,visitor);
    }
    /**
     * Preorder traversal
     */
    public void preorderTraversal(Visitor<E> visitor){
        preorderTraversal(root,visitor);
    }
    public void preorderTraversal(Node<E> node,Visitor<E> visitor){
        if(node==null) return;
        visitor.visit(node.element);
        preorderTraversal(node.left,visitor);
        preorderTraversal(node.right,visitor);
    }
}

② Test:

public class Main {
    public static void main(String[] args) {
        BinarySearchTree<Integer> binarySearchTree = new BinarySearchTree<>();
        int[] arr = {7, 4, 9, 2, 5, 8, 11, 3, 12, 1};
        for(int i=0;i<arr.length;i++){
            binarySearchTree.add(arr[i]);
        }
        BinaryTrees.println(binarySearchTree);

        // Preorder traversal
        binarySearchTree.preorderTraversal(new Visitor<Integer>() {
            @Override
            public void visit(Integer element) {
                System.out.print("_"+element+"_");
            }
        });
        System.out.println("Preorder traversal");
        // Medium order traversal
        binarySearchTree.inorderTraversal(new Visitor<Integer>() {
            @Override
            public void visit(Integer element) {
                System.out.print("_"+element+"_");
            }
        });
        System.out.println("Medium order traversal");
        // Subsequent traversal
        binarySearchTree.postorderTraversal(new Visitor<Integer>() {
            @Override
            public void visit(Integer element) {
                System.out.print("_"+element+"_");
            }
        });
        System.out.println("Postorder traversal");
        // level traversal 
        binarySearchTree.levelOrderTraversal(new Visitor<Integer>() {
            @Override
            public void visit(Integer element) {
                System.out.print("_"+element+"_");
            }
        });
        System.out.println("level traversal ");
    }
}
    ┌──7──┐
    │     │
  ┌─4─┐ ┌─9─┐
  │   │ │   │
┌─2─┐ 5 8   11─┐
│   │          │
1   3          12
_7__4__2__1__3__5__9__8__11__12_Preorder traversal
_1__2__3__4__5__7__8__9__11__12_Medium order traversal
_1__3__2__5__4__8__12__11__9__7_Postorder traversal
_7__4__9__2__5__8__11__1__3__12_level traversal 

6. Enhance the traversal binary tree interface

Demand analysis: after printing to a node, the remaining nodes will not be printed

public class BinarySearchTree<E> implements BinaryTreeInfo {

    // Number of elements
    private int size;

    // Root node
    private Node<E> root;

    /**
     * Traversal interface of binary tree
     */
    public static abstract class Visitor<E>{
        boolean stop;
        /**
         * If it returns true, stop the traversal
         */
        abstract boolean visit(E element);
    }

    /**
     * level traversal 
     */
    public void levelOrderTraversal(Visitor<E> visitor){
        if(root==null ) return;
        Queue<Node<E>> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()){
            Node<E> node = queue.poll();
            if(visitor.visit(node.element)) return;
            if(node.left!=null){
                queue.offer(node.left);
            }
            if(node.right!=null){
                queue.offer(node.right);
            }
        }
    }
    /**
     * Subsequent traversal
     */
    public void postorderTraversal(Visitor<E> visitor){
        if(visitor==null) return;
        postorderTraversal(root,visitor);
    }
    public void postorderTraversal(Node<E> node,Visitor<E> visitor){
        // Stop traversal when visitor=true
        if(node==null || visitor.stop) return;
        postorderTraversal(node.left,visitor);
        postorderTraversal(node.right,visitor);

        if(visitor.stop) return;
        // Store the results of the currently traversed element. For example, when traversing to 4, visitor=true. When traversing down to the next element, it will not be printed
        visitor.stop  = visitor.visit(node.element);
    }
    /**
     * Medium order traversal
     */
    public void inorderTraversal(Visitor<E> visitor){
        if(visitor==null) return;
        inorderTraversal(root,visitor);
    }
    public void inorderTraversal(Node<E> node,Visitor<E> visitor){
        // When traversing the next element, judge stop. If stop=true, it indicates that printing needs to be stopped
        if(node==null || visitor.stop) return;
        inorderTraversal(node.left,visitor);
        if(visitor.stop) return;
        // Save the traversal results of the current element. When traversing the next element, judge stop. If stop=true, it means that printing needs to be stopped
        visitor.stop = visitor.visit(node.element);
        inorderTraversal(node.right,visitor);
    }
    /**
     * Preorder traversal
     */
    public void preorderTraversal(Visitor<E> visitor){
        if(visitor==null) return;
        preorderTraversal(root,visitor);
    }
    public void preorderTraversal(Node<E> node,Visitor<E> visitor){
        if(node==null || visitor.stop) return;
        visitor.stop = visitor.visit(node.element);
        preorderTraversal(node.left,visitor);
        preorderTraversal(node.right,visitor);
    }
}

Test:

public class Main {
    public static void main(String[] args) {
        BinarySearchTree<Integer> binarySearchTree = new BinarySearchTree<>();
        int[] arr = {7, 4, 9, 2, 5, 8, 11, 3, 12, 1};
        for(int i=0;i<arr.length;i++){
            binarySearchTree.add(arr[i]);
        }
        BinaryTrees.println(binarySearchTree);

        // Preorder traversal
        binarySearchTree.preorderTraversal(new Visitor<Integer>() {
            @Override
            public boolean visit(Integer element) {
                System.out.print("_"+element+"_");
                return element==2 ? true : false;
            }
        });
        System.out.println();
        // Medium order traversal
        binarySearchTree.inorderTraversal(new Visitor<Integer>() {
            @Override
            public boolean visit(Integer element) {
                System.out.print("_"+element+"_");
                return element==4 ? true : false;
            }
        });
        System.out.println();
        // Subsequent traversal
        binarySearchTree.postorderTraversal(new Visitor<Integer>() {
            @Override
            public boolean visit(Integer element) {
                System.out.print("_"+element+"_");
                return element==4 ? true : false;
            }
        });
        System.out.println();
        // level traversal 
        binarySearchTree.levelOrderTraversal(new Visitor<Integer>() {
            @Override
            public boolean visit(Integer element) {
                System.out.print("_"+element+"_");
                return element==9 ? true : false;
            }
        });
        System.out.println();
    }
}

7. Find the height of binary tree

public class BinarySearchTree<E> implements BinaryTreeInfo {

    // Number of elements
    private int size;

    // Root node
    private Node<E> root;

    /**
     * Printing the height of a binary tree: a recursive method
     */
    public int height(){
        return height(root);
    }
    private int height(Node<E> node){
        // If node is null, the level is 0
        if(node==null) return 0;
        return Math.max(height(node.left),height(node.right))+1;
    }

    /**
     * Printing the height of a binary tree: an iterative method
     */
    public int height1(){
         if(root==null) return 0;
         Queue<Node<E>> queue = new LinkedList<>();
         queue.add(root);
         int levelSize = 1;
         int height = 0;
         while (!queue.isEmpty()){
             Node<E> node = queue.poll();
             // The number of elements in this layer is reduced by 1
             levelSize--;
             if(node.left!=null) queue.add(node.left);
             if(node.right!=null) queue.add(node.right);
             // The element of this layer has been accessed
             if(levelSize==0){
                 levelSize = queue.size();
                 height++;
             }
         }
         return height;
    }
}

Topics: Linux CentOS network