常见的查找算法(五):树表查找之一 ---- 二叉查找树
二叉查找树(英语:Binary Search Tree),也称为二叉搜索树、有序二叉树(ordered binary tree)或排序二叉树(sorted binary tree),是指一棵空树或者具有下列性质的二叉树:
- 若任意节点的左子树不空,则左子树上所有节点的key值均小于它的根节点的值;
- 若任意节点的右子树不空,则右子树上所有节点的key值均大于它的根节点的值;
- 任意节点的左、右子树也分别为二叉查找树;
- 没有键值相等的节点。
二叉查找树的节点:
1 //二叉树节点 2 static class BSTNode<T> { 3 T data; 4 BSTNode<T> parent; 5 BSTNode<T> left; 6 BSTNode<T> right; 7 8 public BSTNode() { 9 this.data = null; 10 this.left = left; 11 this.right= right; 12 } 13 14 public BSTNode(T data) { 15 this(data,null, null); 16 } 17 18 public BSTNode(T data, BSTNode<T> left, BSTNode<T> right) { 19 this.data = data; 20 this.left = left; 21 this.right = right; 22 } 23 }
查找操作:
这个过程是从树根开始查找,沿着这棵树的一条简单路劲向下进行,根据二叉查找树的性质来比较每个节点的关键字的值,来决定下一个节点的查找方向,若该节点是要查找的关键字,则结束查找过程。
1 /******************************* 查找 start *******************************/ 2 3 /** 4 * 从根节点查找 5 * 6 * @param data 要查找的元素 7 * @return 8 */ 9 public boolean searchBST(T data) { 10 return searchBST(data, root); 11 } 12 13 /** 14 * 递归查找指定元素是否在树中 15 * 16 * @param node 树中节点 17 * @param data 要查找的元素 18 * @return 19 */ 20 public boolean searchBST(T data, BSTNode<T> node) { 21 if (node == null) 22 return false; 23 24 count++; 25 int result = data.compareTo(node.data); 26 27 if (result > 0) { 28 return searchBST(data, node.right);//往右子树查询 29 } else if (result < 0) { 30 return searchBST(data, node.left);//往左子树查询 31 } else { 32 return true; 33 } 34 } 35 36 /** 37 * 寻找二叉树中最大的元素 38 * 39 * @return 40 */ 41 public T findMax() { 42 if (isEmpty()) { 43 System.out.println("二叉树为空"); 44 return null; 45 } else { 46 return findMax(root).data; 47 } 48 } 49 50 /** 51 * 寻找二叉树中最大的元素 52 * 53 * @param node 当前的结点 54 * @return 55 */ 56 public BSTNode<T> findMax(BSTNode<T> node) { 57 if (node == null) { 58 return null; 59 } else if (node.right == null) { 60 return node; 61 } else { 62 //迭代 63 while (node.right != null) { 64 node = node.right; 65 } 66 return node; 67 } 68 } 69 70 /** 71 * 寻找二叉树中最小的元素 72 * 73 * @return 74 */ 75 public T findMin() { 76 if (isEmpty()) { 77 System.out.println("二叉树为空"); 78 return null; 79 } else { 80 return findMin(root).data; 81 } 82 } 83 84 /** 85 * 寻找二叉树中最小的元素 86 * 87 * @param node 当前节点 88 * @return 89 */ 90 public BSTNode<T> findMin(BSTNode<T> node) { 91 if (node == null) 92 return null; 93 else if (node.left == null) 94 return node; 95 return findMin(node.left); 96 } 97 98 /******************************* 查找 end *******************************/
时间复杂度分析:二叉搜索树上的基本操作所花费的时间与这颗树的高度成正比。根据二叉查找树的性质,给定一个元素,去查找树中的元素,是从根节点那开始一层层比较节点的值,从而选定在树中查找的方向。当查找到该元素时,已经比较了n层,也就是元素在树中的层数(根节点为第一层),每层比较一个节点,然而二叉树的高度与它的节点数量有关,为log₂(n+1),所以二叉查找树的朝招时间复杂度为O(log₂n)。
插入操作:
- 若此树是空树,则直接将插入的结点作为根结点插入。
- 插入的值等于插入的值的根结点的数据的值,则直接返回,否则。
- 若插入的值小于此树的根结点的数据的值,则将要插入的结点的位置改变为此树的左子树,否则4。
- 将插入的值的结点的位置改变为此树的右子树。
1 /******************************* 插入 start *******************************/ 2 //插入元素 3 public void insert(T t) { 4 root = insert(t, root); 5 } 6 7 /** 8 * 构建一个二叉查找树 9 * 10 * @param t 插入元素 11 * @param node 插入结点 12 * @return 13 */ 14 public BSTNode<T> insert(T t, BSTNode<T> node) { 15 if (node == null) 16 return new BSTNode<T>(t, null, null); 17 int result = t.compareTo(node.data); 18 if (result < 0) { 19 node.left = insert(t, node); 20 } else if (result > 0) { 21 node.right = insert(t, node); 22 } else { 23 System.out.println("插入失败"); 24 } 25 return node; 26 } 27 /******************************* 插入 end *******************************/
关于前驱和后继
所有的关键字都互不相同,则一个节点的x的后继是大于x.key的最小关键字的结点。
删除操作:
对于二叉查找树的删除操作(这里根据值删除,而非结点)分三种情况:
- 如果结点为叶子结点(没有左、右子树),此时删除该结点不会破坏树的结构,直接删除即可,并修改其父结点指向它的引用为null
- 如果其结点只包含左子树,或者右子树的话,此时直接删除该结点,并将其左子树或者右子树设置为其父结点的左子树或者右子树即可,此操作不会破坏树结构
- 当结点的左、右子树都不空的时候,一般的删除策略是用其右子树的最小数据(容易找到)代替 要删除的结点数据并递归删除该结点(此时为null)
1 /******************************* 删除 start *******************************/ 2 /** 3 * 删除元素 4 * @param t 5 */ 6 public void remove(T t) { 7 root = remove(t, root); 8 } 9 10 /** 11 * 删除某个位置的结点 12 * @param t 13 * @param node 14 * @return 15 */ 16 public BSTNode<T> remove(T t, BSTNode<T> node) { 17 if (node == null) { 18 System.out.println("删除失败"); 19 return node; 20 } 21 int result = t.compareTo(node.data); 22 if (result > 0) { 23 node.right = remove(t, node.right); 24 } else if (result < 0) { 25 node.left = remove(t, node.left); 26 } else if (node.left != null && node.right != null) { 27 node.data = findMin(node.right).data; 28 node.right = remove(node.data, node.right); 29 } else { 30 node = (node.left != null) ? node.left : node.right; 31 } 32 return node; 33 } 34 /******************************* 删除 end *******************************/
遍历二叉查找树(先序遍历):
1 public void preOrder(BSTNode node) { 2 if (node != null) { 3 System.out.print(node.data + " "); 4 preOrder(node.left); 5 preOrder(node.right); 6 } 7 }
测试代码:
1 public static void main(String[] args) { 2 BSTNode<Integer> node3 = new BSTNode<>(3); 3 BSTNode<Integer> node1 = new BSTNode<>(1); 4 BSTNode<Integer> node4 = new BSTNode<>(4, node3, null); 5 BSTNode<Integer> node2 = new BSTNode<>(2, node1, node4); 6 BSTNode<Integer> node8 = new BSTNode<>(8); 7 BSTNode<Integer> root = new BSTNode<>(6, node2, node8); 8 9 BSTree bsTree = new BSTree<>(); 10 bsTree.root = root; 11 bsTree.preOrder(bsTree.root); 12 //bsTree.remove(4); 13 //System.out.println(); 14 //bsTree.preOrder(bsTree.root); 15 bsTree.searchBST(3); 16 System.out.println("查找次数:" + count); 17 }