二叉查找树
-
若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
-
若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
-
它的左、右子树也分别二叉排序树。
java实现二叉查找树节点的定义
1 private class Node { 2 public E e; 3 public Node left, right; 4 5 public Node(E e) { 6 this.e = e; 7 left = null; 8 right = null; 9 } 10 }
1 public class BST<E extends Comparable<E>> { 2 //因为需要对节点进行比较,所以接受的泛型必须实现Comparable<E> 3 private Node root; 4 private int size; 5 6 public BST() { 7 this.root = null; 8 this.size = 0; 9 } 10 11 public int getSize() { 12 return size; 13 } 14 15 public boolean isEmpty() { 16 return size == 0; 17 } 18 }
添加元素到二叉查找树
1 private Node add(Node node, E e) { 2 //这里使用递归的方式来实现 3 if (node == null) { 4 size++; 5 return new Node(e); 6 } 7 8 if (e.compareTo(node.e) < 0) { 9 node.left = add(node.left, e); 10 } else if (e.compareTo(node.e) > 0) { 11 node.right = add(node.right, e); 12 } 13 14 return node; 15 }
1 private boolean contains(Node node, E e) { 2 if (node.e == null) { 3 return false; 4 } 5 6 if (e.compareTo(node.e) < 0) { 7 return contains(node.left, e); 8 } else if (e.compareTo(node.e) > 0) { 9 return contains(node.right, e); 10 } else { //e.compareTo(node.e) == 0 11 return true; 12 } 13 }
-
递归实现(递归的方式很好实现,还好理解。如果对于性能要求不高的话,可以选择递归)
1 /** 2 * 二分搜索树前序遍历-----递归实现 3 * @param node 4 */ 5 private void preOrder(Node node) { 6 if (node == null) { 7 return; 8 } 9 10 System.out.println(node.e);//遍历根节点 11 preOrder(node.left); 12 preOrder(node.right); 13 }
- 非递归实现(非递归的方式相比递归的话,比较不好实现和理解。性能方面比较好)
1 /** 2 * 二分搜索树前序遍历-----非递归实现 3 */ 4 public void proOrderNR() { 5 Stack<Node> stack = new Stack<>(); 6 7 stack.push(root); 8 while (!stack.isEmpty()) { 9 Node cur = stack.pop(); 10 System.out.println(cur.e);//遍历根节点 11 12 if (cur.right != null) 13 stack.push(cur.right); 14 if (cur.left != null) 15 stack.push(cur.left); 16 } 17 }
中序遍历
-
递归实现(递归的方式实现前中后三种遍历,仅仅只需要改动对左右子树递归的顺序即可,所以这里再给出中序遍历后就不给出后序遍历的递归实现了)
1 /** 2 * 二分搜索树中序遍历-----递归实现 3 * @param node 4 */ 5 private void inOrder(Node node) { 6 if (node == null) { 7 return; 8 } 9 10 inOrder(node.left); //相比于前序遍历 11 System.out.println(node.e);//只需要改变遍历的顺序,就可以实现中序遍历了 12 inOrder(node.right); 13 }
- 非递归实现
1 /** 2 * 二分搜索树中序遍历-----非递归实现 3 */ 4 public void inOrderNR() { 5 6 Stack<Node> stack = new Stack<>(); 7 Node cur = root; 8 while (cur != null || !stack.isEmpty()) { 9 while (cur != null) { 10 // *** 11 stack.push(cur);//这是第一次碰到节点 12 cur = cur.left; 13 } 14 cur = stack.pop(); 15 System.out.println(cur.e);//这是第二次碰到,将这条语句放在第一次碰到之前, 16 //函数就变成了前序遍历 17 cur = cur.right; 18 } 19 }
后序遍历
-
非递归实现
1 /** 2 * 二分搜索树后序遍历-----非递归实现 3 */ 4 public void laterOrderNR() { 5 6 Stack<Node> stack = new Stack<>(); 7 Node cur = root; 8 Node right_cur = null; 9 while (cur != null || !stack.isEmpty()) { 10 while (cur != null) { 11 stack.push(cur); 12 cur = cur.left; 13 } 14 cur = stack.pop(); 15 // 当前结点没有右结点或上一个已经输出的结点是当前结点的右结点,则输出当前结点 16 while (cur.right == null || cur.right == right_cur) { 17 System.out.println(cur.e); 18 right_cur = cur; 19 if (stack.isEmpty()) { 20 return; //root已输出,则遍历结束 21 } 22 cur = stack.pop(); 23 } 24 stack.push(cur); //还有右结点没有遍历 25 cur = cur.right; 26 } 27 }
1 /** 2 * 层序遍历 3 */ 4 public void levelOrder(){ 5 Queue<Node> q = new LinkedList<>(); 6 q.add(root); 7 while (!q.isEmpty()){ 8 Node cur = q.remove(); 9 System.out.println(cur.e); 10 11 if (cur.left!=null) 12 q.add(cur.left); 13 14 if (cur.right!=null) 15 q.add(cur.right); 16 17 } 18 }
查找最大和小元素
相反的最大元素就是从根节点向右子树遍历,遍历到最后的节点。
1 //查找最小的元素,这里给出递归的方式 2 public E minFind() { 3 if (size == 0) { 4 throw new IllegalArgumentException("BST is empty!"); 5 } 6 return minFind(root).e; 7 } 8 9 private Node minFind(Node node) { 10 11 if (node.left == null) { 12 return node; 13 } 14 return minFind(node.left); 15 } 16 //查找最大的元素 17 public E maxFind() { 18 if (size == 0) { 19 throw new IllegalArgumentException("BST is empty!"); 20 } 21 return maxFind(root).e; 22 } 23 24 private Node maxFind(Node node) { 25 26 if (node.right == null) { 27 return node; 28 } 29 return maxFind(node.right); 30 }
1 public E removeMin() { 2 E res = minFind(); 3 root = removeMin(root); 4 return res; 5 } 6 7 //删除以node为根的二叉搜索树的最小节点 8 //返回删除节点后新的二叉树的根 9 private Node removeMin(Node node) { 10 if (node.left == null) { 11 Node rightNode = node.right; 12 node.right = null; 13 size--; 14 return rightNode; 15 } 16 node.left = removeMin(node.left); 17 return node; 18 }
删除最大元素逻辑和删除最小元素一样,这里就不再给出了。
-
删除的是叶子节点,像上面的4、7、13 、18这样的没有左右子树的节点。
这种情况下,直接删除就可以了。
-
删除的节点只有左子树或右子树,像3 、 19
这种情况只需要把要删除的节点的子树替代待删除节点的位置就可以。如:4代替3的位置,18代替19的位置
-
删除的节点有左右子树,像10、5 、15.
这种的话需要把待删除的节点的右子树中的最小节点替换到待删除节点的位置,或者把待删除的节点的左子树中的最大节点替换到待删除节点的位置,也就是把待删除节点的后继节点或前驱节点代替被删除节点。如:要删除10这个节点,可以把13或者7移到10的位置。
1 public void remove(E e) { 2 root = remove(root, e); 3 } 4 //这里也是递归实现的,非递归的方式以后会补上 5 //删除值为e以node为根的二叉搜索树的节点 6 //返回删除节点后新的二叉树的根 7 private Node remove(Node node, E e) { 8 if (node == null) { 9 return null; 10 } 11 12 if (e.compareTo(node.e) < 0) { 13 node.left = remove(node.left, e); 14 return node; 15 } else if (e.compareTo(node.e) > 0) { 16 node.right = remove(node.right, e); 17 return node; 18 } else { //e==node.e 19 //待删除节点左子树为空 20 if (node.left == null) { 21 Node rightNode = node.right; 22 node.right = null; 23 size--; 24 return rightNode; 25 } 26 //待删除节点右子树为空 27 if (node.right == null) { 28 Node leftNode = node.left; 29 node.left = null; 30 size--; 31 return leftNode; 32 } 33 //待删除节点左右节点都不为空 34 //找到比待删除节点大的最小节点 35 //用这个节点替换待删除的节点,实现删除 36 Node successor = minFind(node.right); 37 successor.right = removeMin(node.right); 38 successor.left = node.left; 39 node.left = node.right = null; 40 return successor; 41 } 42 }