思考
在 n 个动态的整数中搜索某个整数?(查看其是否存在)
◼ 假设使用动态数组存放元素,从第
0
个位置开始遍历搜索,平均时间复杂度:O(n)
◼ 如果维护一个有序的动态数组,使用二分搜索,最坏时间复杂度:O(logn)
但是添加、删除的平均时间复杂度是 O(n)
◼ 针对这个需求,有没有更好的方案?
使用二叉搜索树,添加、删除、搜索的最坏时间复杂度均可优化至:O(logn)
二叉搜索树(Binar y Search Tree)
简介
- 二叉搜索树是二叉树的一种,是应用非常广泛的一种二叉树,英文简称为 BST
又被称为:二叉查找树、二叉排序树
- 任意一个结点的值都大于其左子树所有结点的值
- 任意一个结点的值都小于其右子树所有结点的值
- 它的左右子树也是一棵二叉搜索树
二叉搜索树可以大大提高搜索数据的效率
二叉搜索树存储的元素必须具备可比较性
比如int
、double
等
如果是自定义类型,需要指定比较方式
不允许为null
二叉搜索树的接口设计
◼ int size()
// 元素的数量
◼ boolean isEmpty()
// 是否为空
◼ void clear()
// 清空所有元素
◼ void add(E element)
// 添加元素
◼ void remove(E element)
// 删除元素
◼ boolean contains(E element)//
是否包含某元素
对于我们现在使用的二叉树来说,它的元素没有索引的概念
添加结点
例如:添加12、1
思路
二叉搜索树实现元素的添加肯定要从root
结点元素开始比较,如果比root
结点元素大,就继续跟右子树结点比较,如果比root
结点元素小,就跟左子树结点比较,直到找到该元素的父结点,在判断是添加到左子树还是右子树
如果添加的元素是个类别,如何实现要添加元素跟结点元素之间的比较?
1. 定义公共接口
package njf;
public interface Comparable {
int compareTo(E e);
}
二叉搜索树实现结点的添加
package njf;
@SuppressWarnings("unused")
public class BinarySearchTree {
private int size;
private Node root;
private static class Node{
E element;
Node left;//左子树
Node right;//右子树
Node parent;//父结点
public Node(E element,Node parent) {
this.element = element;
this.parent = parent;
}
}
public void add(E element) {
elementNotNullCheck(element);
//添加第一个根结点
if (root == null) {
root = new Node<>(element, null);
size ++;
return;
}
// 添加的不是第一个结点
// 找到父结点
Node node = root;
Node parent = root;
int cmp = 0;
while (node != null) {
parent = node;
cmp = compare(element, node.element);
if (cmp > 0) {
node = node.right;
}else if (cmp < 0) {
node = node.left;
}else {
return;
}
}
// 看看插入到父结点的哪个位置
Node newNode = new Node<>(element, parent);
if (cmp > 0) {
parent.right = newNode;
}else{
parent.left = newNode;
}
size ++;
}
/**
* @return 返回值等于0,代表e1和e2相等;返回值大于0,代表e1大于e2;返回值小于于0,代表e1小于e2
*/
private int compare(E e1,E e2) {
return e1.compareTo(e2);
}
private void elementNotNullCheck(E element) {
if (element == null) {
throw new IllegalArgumentException("element must not be null");
}
}
}
定义要添加的person类别
package njf;
public class Person implements Comparable {
private int age;
public Person(int age) {
this.age = age;
}
@Override
public int compareTo(Person e) {
return age - e.age;
}
}
代码的调用
package njf;
public class Main {
public static void main(String[] args) {
BinarySearchTree bst1 = new BinarySearchTree<>();
bst1.add(new Person(5));
bst1.add(new Person(6));
}
}
如果还有需求是当添加Person.age
比它父结点的age
大时,添加到左子树呢,比它父结点的age
小时,添加到右子树呢?这个时候定义接口显然不能满足多种情况,我们可以设置比较器
2. 设置比较器
package njf;
public interface Comparator {
int compare(E e1,E e2);
}
二叉搜索树实现结点的添加
package njf;
@SuppressWarnings("unused")
public class BinarySearchTree {
private int size;
private Node root;
private Comparator comparator;
public BinarySearchTree() {
this(null);
}
public BinarySearchTree(Comparator comparator) {
this.comparator = comparator;
}
private static class Node{
E element;
Node left;//左子树
Node right;//右子树
Node parent;//父结点
public Node(E element,Node parent) {
this.element = element;
this.parent = parent;
}
}
public void add(E element) {
elementNotNullCheck(element);
//添加第一个根结点
if (root == null) {
root = new Node<>(element, null);
size ++;
return;
}
// 添加的不是第一个结点
// 找到父结点
Node node = root;
Node parent = root;
int cmp = 0;
while (node != null) {
parent = node;
cmp = compare(element, node.element);
if (cmp > 0) {
node = node.right;
}else if (cmp < 0) {
node = node.left;
}else {
return;
}
}
// 看看插入到父结点的哪个位置
Node newNode = new Node<>(element, parent);
if (cmp > 0) {
parent.right = newNode;
}else{
parent.left = newNode;
}
size ++;
}
/**
* @return 返回值等于0,代表e1和e2相等;返回值大于0,代表e1大于e2;返回值小于于0,代表e1小于e2
*/
private int compare(E e1,E e2) {
if (comparator != null) {
return comparator.compare(e1,e2);
}
return ((Comparable)e1).compareTo(e2);
}
private void elementNotNullCheck(E element) {
if (element == null) {
throw new IllegalArgumentException("element must not be null");
}
}
}
定义要添加的person类别
package njf;
public class Person implements Comparable {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Person(int age) {
this.age = age;
}
@Override
public int compareTo(Person e) {
return age - e.age;
}
@Override
public String toString() {
return age + "_";
}
}
代码调用
package njf;
import java.util.Comparator;
public class Main {
private static class PersonComparator implements Comparator{
public int compare(Person e1, Person e2) {
return e1.getAge() - e2.getAge();
}
}
public static void main(String[] args) {
BinarySearchTree bst1 = new BinarySearchTree<>(new PersonComparator());
bst1.add(new Person(5));
bst1.add(new Person(6));
/*
* Java的匿名类,类似于iOS中的Block、JS中的闭包(function)
*/
BinarySearchTree bst2 = new BinarySearchTree<>(new Comparator() {
@Override
public int compare(Person o1, Person o2) {
return o2.getAge() - o1.getAge();
}
});
bst2.add(new Person(5));
bst2.add(new Person(6));
BinarySearchTree bst3 = new BinarySearchTree<>();
bst3.add(new Person(5));
bst3.add(new Person(6));
}
}
细节
/**
* @return 返回值等于0,代表e1和e2相等;返回值大于0,代表e1大于e2;返回值小于于0,代表e1小于e2
*/
private int compare(E e1,E e2) {
if (comparator != null) {
return comparator.compare(e1,e2);
}
return ((Comparable)e1).compareTo(e2);
}
public class BinarySearchTree
这里可以看到比较方法里面,判断了两种情况,当外面调用传比较器时,走的是比较器方法,如果没有传比较器,走的是定义的接口,而且类别没有遵循接口,是对e1进强致遵循接口Comparable
接口和比较器都可以用java官方的 java.util.Comparator 和 java.lang.Comparable
打印器
工具类:
二叉树打印器
二叉搜索树实现打印树结构
package njf;
import java.util.Comparator;
import njf.printer.BinaryTreeInfo;
@SuppressWarnings("unused")
public class BinarySearchTree implements BinaryTreeInfo{
private int size;
private Node root;
private Comparator comparator;
public BinarySearchTree() {
this(null);
}
public BinarySearchTree(Comparator comparator) {
this.comparator = comparator;
}
private static class Node{
E element;
Node left;//左子树
Node right;//右子树
Node parent;//父结点
public Node(E element,Node parent) {
this.element = element;
this.parent = parent;
}
}
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
public void clear() {
}
public void add(E element) {
elementNotNullCheck(element);
//添加第一个根结点
if (root == null) {
root = new Node<>(element, null);
size ++;
return;
}
// 添加的不是第一个结点
// 找到父结点
Node node = root;
Node parent = root;
int cmp = 0;
while (node != null) {
parent = node;
cmp = compare(element, node.element);
if (cmp > 0) {
node = node.right;
}else if (cmp < 0) {
node = node.left;
}else {
return;
}
}
// 看看插入到父结点的哪个位置
Node newNode = new Node<>(element, parent);
if (cmp > 0) {
parent.right = newNode;
}else{
parent.left = newNode;
}
size ++;
}
/**
* @return 返回值等于0,代表e1和e2相等;返回值大于0,代表e1大于e2;返回值小于于0,代表e1小于e2
*/
private int compare(E e1,E e2) {
if (comparator != null) {
return comparator.compare(e1,e2);
}
return ((Comparable)e1).compareTo(e2);
}
private void elementNotNullCheck(E element) {
if (element == null) {
throw new IllegalArgumentException("element must not be null");
}
}
@Override
public Object root() {
return root;
}
@Override
public Object left(Object node) {
return ((Node)node).left;
}
@Override
public Object right(Object node) {
return ((Node)node).right;
}
@Override
public Object string(Object node) {
Node myNode = (Node)node;
String parentString = "null";
if (myNode.parent != null) {
parentString = myNode.parent.element.toString();
}
return myNode.element + "_p(" + parentString + ")";
}
}
调用如下
package njf;
import java.util.Comparator;
import njf.printer.BinaryTreeInfo;
import njf.printer.BinaryTrees;
import njf.file.Files;
public class Main {
static void test1() {
Integer data[] = new Integer[] {
7, 4, 9, 2, 5, 8, 11, 3, 12, 1
};
BinarySearchTree bst = new BinarySearchTree<>();
for (int i = 0; i < data.length; i++) {
bst.add(data[i]);
}
BinaryTrees.println(bst);
}
static void test2() {
Integer data[] = new Integer[] {
7, 4, 9, 2, 5, 8, 11, 3, 12, 1
};
/*
* Java的匿名类,类似于iOS中的Block、JS中的闭包(function)
*/
BinarySearchTree bst = new BinarySearchTree<>(new Comparator() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge() - o2.getAge();
}
});
for (int i = 0; i < data.length; i++) {
bst.add(new Person(data[i]));
}
BinaryTrees.println(bst);
BinarySearchTree bst1 = new BinarySearchTree<>(new Comparator() {
@Override
public int compare(Person o1, Person o2) {
return o2.getAge() - o1.getAge();
}
});
for (int i = 0; i < data.length; i++) {
bst1.add(new Person(data[i]));
}
BinaryTrees.println(bst1);
}
static void test3() {
BinarySearchTree bst = new BinarySearchTree<>();
for (int i = 0; i < 40; i++) {
bst.add((int)(Math.random() * 100));
}
String str = BinaryTrees.printString(bst);
str += "\n";
Files.writeToFile("/Users/niujf/Desktop/:1.txt", str, true);
BinaryTrees.println(bst);
}
static void test4() {
BinaryTrees.println(new BinaryTreeInfo() {
@Override
public Object string(Object node) {
return node.toString() + "_";
}
@Override
public Object root() {
return "A";
}
@Override
public Object right(Object node) {
if (node.equals("A")) return "C";
if (node.equals("C")) return "E";
return null;
}
@Override
public Object left(Object node) {
if (node.equals("A")) return "B";
if (node.equals("C")) return "D";
return null;
}
});
}
public static void main(String[] args) {
test1();
}
}
结果如下:
┌───7_p(null)───┐
│ │
┌─4_p(7)─┐ ┌─9_p(7)─┐
│ │ │ │
┌─2_p(4)─┐ 5_p(4) 8_p(9) 11_p(9)─┐
│ │ │
1_p(2) 3_p(2) 12_p(11)
删除结点
1. 叶子节点
◼ 直接删除
- 如果node == node.parent.left
node.parent.left = null
- 如果node == node.parent .right
node.parent.right = null
- 如果node.parent == null
root = null
2.度为1的节点
用子节点替代原节点的位置
child
是 node.left
或者 child
是 node.right
- 如果 node 是左子节点
➢ child.parent = node.parent
➢ node.parent.left = child
- 如果 node 是右子节点
➢ child.parent = node.parent
➢ node.parent.right = child
- 如果 node 是根节点
➢ root = child
➢ child.parent = null
3.度为2的节点
举例:先删除 5、再删除 4
◼ 先用前驱或者后继节点的值覆盖原节点的值
◼ 然后删除相应的前驱或者后继节点
如果一个节点的度为 2
那么它的前驱、后继节点的度只可能是 1 和 0
代码实现
public void remove(E element) {
remove(node(element));
}
private Node remove(Node node){
if (node == null) return null;
size --;
if (node.left != null && node.right != null) {//度为2的结点
// 找到后继节点
Node s = successor(node);
// 用后继节点的值覆盖度为2的节点的值
node.element = s.element;
// 删除后继节点
node = s;
}
// 删除node节点(node的度必然是1或者0)
Node replacement = node.left!= null ? node.left : node.right;
//replacement 可能是左子结点/右子结点
if (replacement != null) {// node是度为1的节点
replacement.parent = node.parent;
// 更改parent的left、right的指向
if (node.parent == null) {
root = replacement;
}else if (node == node.parent.left) {//左子结点
node.parent.left = replacement;
}else{// node == node.parent.right
node.parent.right = replacement;
}
}else if (node.parent == null) {// node是叶子节点并且是根节点
root = null;
}else {
if (node == node.parent.left) {
node.parent.left = null;
}else {// node == node.parent.right
node.parent.right = null;
}
}
return node;
}
/**
* 找到当前结点
*/
private Node node(E element){
Node node = root;
while (node!= null) {
int cmp = compare(element, node.element);
if (cmp == 0) return node;
if (cmp > 0) {
node = node.right;
}else {
node = node.left;
}
}
return null;
}
代码调用
package njf;
import java.util.Comparator;
import njf.printer.BinaryTreeInfo;
import njf.printer.BinaryTrees;
import njf.BinarySearchTree.Visitor;
import njf.file.Files;
public class Main {
static void test8() {
Integer data[] = new Integer[] {
7, 4, 9, 2, 5, 8, 11, 3, 12, 1
};
BinarySearchTree bst = new BinarySearchTree<>();
for (int i = 0; i < data.length; i++) {
bst.add(data[i]);
}
BinaryTrees.println(bst);
bst.remove(9);
System.out.println("\n");
BinaryTrees.println(bst);
}
public static void main(String[] args) {
test8();
}
}
可以看到删除一个度为2的结点9的前后树结构如下
┌───7_p(null)───┐
│ │
┌─4_p(7)─┐ ┌─9_p(7)─┐
│ │ │ │
┌─2_p(4)─┐ 5_p(4) 8_p(9) 11_p(9)─┐
│ │ │
1_p(2) 3_p(2) 12_p(11)
┌───7_p(null)────┐
│ │
┌─4_p(7)─┐ ┌─11_p(7)─┐
│ │ │ │
┌─2_p(4)─┐ 5_p(4) 8_p(11) 12_p(11)
│ │
1_p(2) 3_p(2)
二叉搜索树的完整代码
package njf;
import java.util.Comparator;
import java.util.IllegalFormatCodePointException;
import java.util.LinkedList;
import java.util.Queue;
import javax.swing.plaf.basic.BasicInternalFrameTitlePane.MaximizeAction;
import njf.printer.BinaryTreeInfo;
@SuppressWarnings("unused")
public class BinarySearchTree implements BinaryTreeInfo{
private int size;
private Node root;
private Comparator comparator;
public BinarySearchTree() {
this(null);
}
public BinarySearchTree(Comparator comparator) {
this.comparator = comparator;
}
private static class Node{
E element;
Node left;//左子树
Node right;//右子树
Node parent;//父结点
public Node(E element,Node parent) {
this.element = element;
this.parent = parent;
}
public boolean isLeaf() {
return left == null && right == null;
}
public boolean hasTwoChildren() {
return left != null && right != null;
}
}
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
public void clear() {
root = null;
size = 0;
}
public boolean contains(E element) {
return node(element) != null;
}
public void add(E element) {
elementNotNullCheck(element);
//添加第一个根结点
if (root == null) {
root = new Node<>(element, null);
size ++;
return;
}
// 添加的不是第一个结点
// 找到父结点
Node node = root;
Node parent = root;
int cmp = 0;
while (node != null) {
parent = node;
cmp = compare(element, node.element);
if (cmp > 0) {
node = node.right;
}else if (cmp < 0) {
node = node.left;
}else {
return;
}
}
// 看看插入到父结点的哪个位置
Node newNode = new Node<>(element, parent);
if (cmp > 0) {
parent.right = newNode;
}else{
parent.left = newNode;
}
size ++;
}
public void remove(E element) {
remove(node(element));
}
private Node remove(Node node){
if (node == null) return null;
size --;
if (node.hasTwoChildren()) {//度为2的结点
// 找到后继节点
Node s = successor(node);
// 用后继节点的值覆盖度为2的节点的值
node.element = s.element;
// 删除后继节点
node = s;
}
// 删除node节点(node的度必然是1或者0)
Node replacement = node.left!= null ? node.left : node.right;
//replacement 可能是左子结点/右子结点
if (replacement != null) {// node是度为1的节点
replacement.parent = node.parent;
// 更改parent的left、right的指向
if (node.parent == null) {
root = replacement;
}else if (node == node.parent.left) {//左子结点
node.parent.left = replacement;
}else{// node == node.parent.right
node.parent.right = replacement;
}
}else if (node.parent == null) {// node是叶子节点并且是根节点
root = null;
}else {
if (node == node.parent.left) {
node.parent.left = null;
}else {// node == node.parent.right
node.parent.right = null;
}
}
return node;
}
/**
* 找到当前结点
*/
private Node node(E element){
Node node = root;
while (node!= null) {
int cmp = compare(element, node.element);
if (cmp == 0) return node;
if (cmp > 0) {
node = node.right;
}else {
node = node.left;
}
}
return null;
}
/**
* 是否为前驱结点
*/
private Node predecessor(Node node) {
if (node == null) return node;
// 前驱节点在左子树当中(left.right.right.right....)
Node p = node.left;
if (p != null) {
while (p.right != null) {
p = p.right;
}
return p;
}
// 从父结点、祖父节点中寻找前驱节点
while (node.parent != null && node.parent.left == node) {
node = node.parent;
}
// node.parent == null
// node == node.parent.right
return node.parent;
}
/**
* 是否为后继结点
*/
private Node successor(Node node) {
if (node == null) return node;
// 后继节点在右子树当中(right.left.left.left....)
Node p = node.right;
if (p != null) {
while (p.left != null) {
p = p.left;
}
return p;
}
// 从父结点、祖父节点中寻找前驱节点
while (node.parent != null && node.parent.right == node) {
node = node.parent;
}
// node.parent == null
// node == node.parent.left
return node.parent;
}
/**
* 是否为完全二叉树
*/
public boolean isCompleteTree() {
if (root == null) return false;
Queue> queue = new LinkedList<>();
queue.offer(root);
boolean isLeaf = false;
while (!queue.isEmpty()) {
Node node = queue.poll();
//如果出现叶子结点,但是后面结点又不是叶子结点,false
if (isLeaf && !(node.isLeaf())) return false;
if (node.left != null) {
queue.offer(node.left);
}else if(node.right != null) {// node.left == null && node.right != null
return false;
}
if(node.right != null) {
queue.offer(node.right);
}else {// node.right == null
isLeaf = true;
}
}
return true;
}
public int height2() {
return height(root);
}
/**
* 计算二叉树的高度,利用递归调用
*/
private int height(Node node) {
if (node == null) return 0;
return 1 + Math.max(height(node.left), height(node.right));
}
/**
* 计算二叉树的高度,利用层序遍历
*/
public int height() {
if (root == null) return 0;
// 树的高度
int height = 0;
// 存储着每一层的元素数量
int levelSize = 1;
Queue> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
Node node = queue.poll();
levelSize --;
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
if (levelSize == 0) {// 意味着即将要访问下一层
levelSize = queue.size();
height ++;
}
}
return height;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
toString(root, sb, "");
return sb.toString();
}
private void toString(Node node, StringBuilder sb, String prefix) {
if (node == null) return;
toString(node.left, sb, prefix + "L---");
sb.append(prefix).append(node.element).append("\n");
toString(node.right, sb, prefix + "R---");
}
// /**
// * 前序遍历
// */
// public void preorderTraversal() {
// preorderTraversal(root);
// }
//
// private void preorderTraversal(Node node) {
// if (node == null) return;
// System.out.println(node.element);
// preorderTraversal(node.left);
// preorderTraversal(node.right);
// }
//
// /**
// * 中序遍历
// */
// public void inorderTraversal() {
// inorderTraversal(root);
// }
//
// private void inorderTraversal(Node node) {
// if (node == null) return;
// inorderTraversal(node.left);
// System.out.println(node.element);
// inorderTraversal(node.right);
// }
// /**
// * 后序遍历
// */
// public void postorderTraversal() {
// postorderTraversal(root);
// }
//
// private void postorderTraversal(Node node) {
// if (node == null) return;
// postorderTraversal(node.left);
// postorderTraversal(node.right);
// System.out.println(node.element);
// }
// /**
// * 层序遍历
// */
// public void levelOrderTraversal(){
// if (root == null) return;
// Queue> queue = new LinkedList<>();
// queue.offer(root);
// while (!queue.isEmpty()) {
// Node node = queue.poll();
// System.out.println(node.element);
// if (node.left != null) {
// queue.offer(node.left);
// }
// if (node.right != null) {
// queue.offer(node.right);
// }
// }
// }
/**
* 前序遍历
*/
public void preorderTraversal(Visitor visitor) {
if (visitor == null) return;
preorderTraversal(root,visitor);
}
private void preorderTraversal(Node node,Visitor visitor) {
if (node == null || visitor.stop) return;
visitor.stop = visitor.visit(node.element);
preorderTraversal(node.left,visitor);
preorderTraversal(node.right,visitor);
return;
}
/**
* 中序遍历
*/
public void inorderTraversal(Visitor visitor) {
if (visitor == null) return;
inorderTraversal(root,visitor);
}
private void inorderTraversal(Node node,Visitor visitor) {
if (node == null || visitor.stop) return;
inorderTraversal(node.left,visitor);
if (visitor.stop) return;
visitor.stop = visitor.visit(node.element);
inorderTraversal(node.right,visitor);
}
/**
* 后序遍历
*/
public void postorderTraversal(Visitor visitor) {
if (visitor == null) return;
postorderTraversal(root,visitor);
}
private void postorderTraversal(Node node,Visitor visitor) {
if (node == null || visitor.stop) return;
postorderTraversal(node.left,visitor);
postorderTraversal(node.right,visitor);
if (visitor.stop) return;
visitor.stop = visitor.visit(node.element);
}
/**
* 层序遍历
*/
public void levelOrderTraversal(Visitor visitor){
if (root == null || visitor == null) return;
Queue> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
Node 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);
}
}
}
public static abstract class Visitor {
boolean stop;
/**
* @return 如果返回true,就代表停止遍历
*/
public abstract boolean visit(E elemet);
}
/**
* @return 返回值等于0,代表e1和e2相等;返回值大于0,代表e1大于e2;返回值小于于0,代表e1小于e2
*/
private int compare(E e1,E e2) {
if (comparator != null) {
return comparator.compare(e1,e2);
}
return ((Comparable)e1).compareTo(e2);
}
private void elementNotNullCheck(E element) {
if (element == null) {
throw new IllegalArgumentException("element must not be null");
}
}
@Override
public Object root() {
return root;
}
@Override
public Object left(Object node) {
return ((Node)node).left;
}
@Override
public Object right(Object node) {
return ((Node)node).right;
}
@Override
public Object string(Object node) {
Node myNode = (Node)node;
String parentString = "null";
if (myNode.parent != null) {
parentString = myNode.parent.element.toString();
}
return myNode.element + "_p(" + parentString + ")";
}
}
代码重构
普通二叉树
package tree;
import java.util.Comparator;
@SuppressWarnings("unchecked")
public class BST extends BinaryTree {
private Comparator comparator;
public BST() {
this(null);
}
public BST(Comparator comparator) {
this.comparator = comparator;
}
public void add(E element) {
elementNotNullCheck(element);
// 添加第一个节点
if (root == null) {
root = new Node<>(element, null);
size++;
return;
}
// 添加的不是第一个节点
// 找到父节点
Node parent = root;
Node node = root;
int cmp = 0;
do {
cmp = compare(element, node.element);
parent = node;
if (cmp > 0) {
node = node.right;
} else if (cmp < 0) {
node = node.left;
} else { // 相等
node.element = element;
return;
}
} while (node != null);
// 看看插入到父节点的哪个位置
Node newNode = new Node<>(element, parent);
if (cmp > 0) {
parent.right = newNode;
} else {
parent.left = newNode;
}
size++;
}
public void remove(E element) {
remove(node(element));
}
public boolean contains(E element) {
return node(element) != null;
}
private void remove(Node node) {
if (node == null) return;
size--;
if (node.hasTwoChildren()) { // 度为2的节点
// 找到后继节点
Node s = successor(node);
// 用后继节点的值覆盖度为2的节点的值
node.element = s.element;
// 删除后继节点
node = s;
}
// 删除node节点(node的度必然是1或者0)
Node replacement = node.left != null ? node.left : node.right;
if (replacement != null) { // node是度为1的节点
// 更改parent
replacement.parent = node.parent;
// 更改parent的left、right的指向
if (node.parent == null) { // node是度为1的节点并且是根节点
root = replacement;
} else if (node == node.parent.left) {
node.parent.left = replacement;
} else { // node == node.parent.right
node.parent.right = replacement;
}
} else if (node.parent == null) { // node是叶子节点并且是根节点
root = null;
} else { // node是叶子节点,但不是根节点
if (node == node.parent.left) {
node.parent.left = null;
} else { // node == node.parent.right
node.parent.right = null;
}
}
}
private Node node(E element) {
Node node = root;
while (node != null) {
int cmp = compare(element, node.element);
if (cmp == 0) return node;
if (cmp > 0) {
node = node.right;
} else { // cmp < 0
node = node.left;
}
}
return null;
}
/**
* @return 返回值等于0,代表e1和e2相等;返回值大于0,代表e1大于e2;返回值小于于0,代表e1小于e2
*/
private int compare(E e1, E e2) {
if (comparator != null) {
return comparator.compare(e1, e2);
}
return ((Comparable)e1).compareTo(e2);
}
private void elementNotNullCheck(E element) {
if (element == null) {
throw new IllegalArgumentException("element must not be null");
}
}
}
二叉搜索树
package tree;
import java.util.LinkedList;
import java.util.Queue;
import njf.printer.BinaryTreeInfo;
@SuppressWarnings("unchecked")
public class BinaryTree implements BinaryTreeInfo {
protected int size;
protected Node root;
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
public void clear() {
root = null;
size = 0;
}
public void preorder(Visitor visitor) {
if (visitor == null) return;
preorder(root, visitor);
}
private void preorder(Node node, Visitor visitor) {
if (node == null || visitor.stop) return;
visitor.stop = visitor.visit(node.element);
preorder(node.left, visitor);
preorder(node.right, visitor);
}
public void inorder(Visitor visitor) {
if (visitor == null) return;
inorder(root, visitor);
}
private void inorder(Node node, Visitor visitor) {
if (node == null || visitor.stop) return;
inorder(node.left, visitor);
if (visitor.stop) return;
visitor.stop = visitor.visit(node.element);
inorder(node.right, visitor);
}
public void postorder(Visitor visitor) {
if (visitor == null) return;
postorder(root, visitor);
}
private void postorder(Node node, Visitor visitor) {
if (node == null || visitor.stop) return;
postorder(node.left, visitor);
postorder(node.right, visitor);
if (visitor.stop) return;
visitor.stop = visitor.visit(node.element);
}
public void levelOrder(Visitor visitor) {
if (root == null || visitor == null) return;
Queue> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
Node 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);
}
}
}
public boolean isComplete() {
if (root == null) return false;
Queue> queue = new LinkedList<>();
queue.offer(root);
boolean leaf = false;
while (!queue.isEmpty()) {
Node node = queue.poll();
if (leaf && !node.isLeaf()) return false;
if (node.left != null) {
queue.offer(node.left);
} else if (node.right != null) {
return false;
}
if (node.right != null) {
queue.offer(node.right);
} else { // 后面遍历的节点都必须是叶子节点
leaf = true;
}
}
return true;
}
public int height() {
if (root == null) return 0;
// 树的高度
int height = 0;
// 存储着每一层的元素数量
int levelSize = 1;
Queue> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
Node node = queue.poll();
levelSize--;
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
if (levelSize == 0) { // 意味着即将要访问下一层
levelSize = queue.size();
height++;
}
}
return height;
}
public int height2() {
return height(root);
}
private int height(Node node) {
if (node == null) return 0;
return 1 + Math.max(height(node.left), height(node.right));
}
protected Node predecessor(Node node) {
if (node == null) return null;
// 前驱节点在左子树当中(left.right.right.right....)
Node p = node.left;
if (p != null) {
while (p.right != null) {
p = p.right;
}
return p;
}
// 从父节点、祖父节点中寻找前驱节点
while (node.parent != null && node == node.parent.left) {
node = node.parent;
}
// node.parent == null
// node == node.parent.right
return node.parent;
}
protected Node successor(Node node) {
if (node == null) return null;
// 前驱节点在左子树当中(right.left.left.left....)
Node p = node.right;
if (p != null) {
while (p.left != null) {
p = p.left;
}
return p;
}
// 从父节点、祖父节点中寻找前驱节点
while (node.parent != null && node == node.parent.right) {
node = node.parent;
}
return node.parent;
}
public static abstract class Visitor {
boolean stop;
/**
* @return 如果返回true,就代表停止遍历
*/
abstract boolean visit(E element);
}
protected static class Node {
E element;
Node left;
Node right;
Node parent;
public Node(E element, Node parent) {
this.element = element;
this.parent = parent;
}
public boolean isLeaf() {
return left == null && right == null;
}
public boolean hasTwoChildren() {
return left != null && right != null;
}
}
@Override
public Object root() {
return root;
}
@Override
public Object left(Object node) {
return ((Node)node).left;
}
@Override
public Object right(Object node) {
return ((Node)node).right;
}
@Override
public Object string(Object node) {
Node myNode = (Node)node;
String parentString = "null";
if (myNode.parent != null) {
parentString = myNode.parent.element.toString();
}
return myNode.element + "_p(" + parentString + ")";
}
}
二叉搜索树的复杂度分析
如果是按照 7、4、9、2、5、8、11 的顺序添加节点
O(h) == O(logn)
如果是从小到大添加节点
O(h) == O(n) 二叉搜索树退化成链表
网站推荐
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.codelik in