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; } }