搜索树是一种很常用的数据结构,支持search、minimum、maximum、predecessor、successor、insert和delete等多种动态集合的操作,经常被用来做字典或优先级队列。binary search tree的所有操作都与树的高度成正比,也就是O(h)。我们称一颗二叉树是平衡的——如果二叉树是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
本章着重介绍了二叉查找树,下面几章分别介绍了几种常见的平衡二叉树,包括红黑树、B树和AVL树(思考题中)。
binary search tree的定义是,对于每个结点,其左子树的所有节点必须满足left[k]<=k,其右子树的所有结点必须满足right[k]>=k。这个定义是以下所有算法的基础。
search:查找一个指定结点。从根(或任意位置)开始,比较key值,如果结点的key小于被当前结点,查找左子树,否则查找右子树。如果超出叶结点,则待查找的结点不存在。
minimum:从根(或任意位置)开始,不断查找左儿子直到遇到叶结点。
maximum:类似,不断查找右儿子。
predecessor:前驱。一个结点的前驱(即有序化后其前面的结点)是其左子树的最大值。如果不存在左子树,那么向上查找父节点,直到某个节点是其父节点的右结点为止。如果不存在这样的结点,那么该节点就是整棵树的最小值。
successor:后继。类似地,一个节点的后继是其右子树的最小值。如果不存在右子树,那么向上查找父节点,直到某个节点是其父节点的左结点为止。
insert:插入的过程类似查找。先沿根节点(或任意结点)开始查找,如果当前结点大于待插入结点的key值,那么递归查找其右子树,如果小于,那么查找其左子树,重复以上过程直到遇到相等的结点或者叶子结点。然后根据确定的结点和待插入结点key的大小关系,选择正确的位置予以插入。
delete:删除的过程最为复杂。如果待删除的结点(仍然要先搜索)拥有0或1个子节点,那么可以通过删除链表结点的方法删除该结点,否则,必须用其前驱或后缀结点代替该结点,然后删除其前驱或后缀结点(可以证明其前驱和后缀结点最多拥有一个子节点)。
相关C程序(未经过调试):
1 #ifndef BINARY_NODE_INCLUDED_ 2 #define BINARY_NODE_INCLUDED_ 3 4 struct node { 5 struct node *parent; 6 struct node *left; 7 struct node *right; 8 void *value; 9 }; 10 11 #include <assert.h> 12 #include <malloc.h> 13 14 15 typedef struct node node; 16 17 typedef int (*compare)(void *n1, void *n2); 18 19 typedef struct binary_tree { 20 node *root; 21 size_t size; 22 } binary_tree; 23 24 node *binary_tree_minimum(const node *nd) { 25 assert(nd); 26 while (nd->left) { 27 nd = nd->left; 28 } 29 return nd; 30 } 31 32 node *binary_tree_maximum(const node *nd) { 33 assert(nd); 34 while (nd->right)nd = nd->right; 35 return nd; 36 } 37 38 node *binary_tree_search(const node *nd, void *value, compare cp) { 39 assert(nd); 40 int cp_result; 41 while (nd && (cp_result = cp(nd->value, value))) { 42 if (cp_result < 0) 43 nd = nd->left; 44 else 45 nd = nd->right; 46 } 47 if (!nd) 48 return NULL; 49 return nd; 50 } 51 52 node *binary_node_precursor(const node *nd) { 53 assert(nd); 54 if (nd->left) 55 return binary_tree_maximum(nd->left); 56 while (nd == nd->parent->right)nd = nd->parent; 57 return nd; 58 } 59 60 node *binary_node_succssor(const node *nd) { 61 assert(nd); 62 if (nd->right) 63 return binary_tree_minimum(nd->right); 64 while (nd == nd->parent->left)nd = nd->left; 65 return nd; 66 } 67 68 node *binary_tree_insert(binary_tree *tree, void *value, compare cp) { 69 assert(tree); 70 tree->size += 1; 71 node *nd = tree->root; 72 node *newnode = (node *) malloc(sizeof(node)); 73 assert(newnode); 74 newnode->left = NULL; 75 newnode->right = NULL; 76 newnode->parent = NULL; 77 newnode->value = value; 78 if (!nd) { 79 tree->root = newnode; 80 return NULL; 81 } 82 node *pre = NULL; 83 int cp_result; 84 while (nd && (cp_result = cp(nd->value, value))) { 85 pre = nd; 86 if (cp_result < 0)nd = nd->right; 87 else 88 nd = nd->left; 89 } 90 newnode->parent = pre; 91 if (cp(pre->value, value) < 0) 92 pre->right = newnode; 93 else 94 pre->left = newnode; 95 return newnode; 96 } 97 98 void binary_tree_delete(binary_tree *tree, void *value, compare cp) { 99 assert(tree); 100 node *nd = tree->root; 101 node *bak = nd; 102 if (!nd)return; 103 nd = binary_tree_search(nd, value, cp); 104 if (!nd)return; 105 int i = (nd->left != NULL) + (nd->right != NULL); 106 if (i > 1)nd = binary_node_succssor(nd); 107 if (nd->left) { 108 nd->left->parent = nd->parent; 109 nd->parent->left = nd->left; 110 } else { 111 nd->right->parent = nd->parent; 112 nd->parent->right = nd->right; 113 } 114 nd->left = 0; 115 nd->right = 0; 116 nd->parent = 0; 117 if (nd != bak)bak->value = nd->value; 118 free(nd); 119 } 120 121 binary_tree *binary_tree_cretae() { 122 binary_tree *tree = (binary_tree *)malloc(sizeof(binary_tree)); 123 if (!tree)return NULL; 124 tree->size = 0; 125 tree->root = NULL; 126 return tree; 127 } 128 129 void postorder_traversal(node *nd, void visit(node *)) { 130 if (!nd)return; 131 postorder_traversal(nd->left, visit); 132 postorder_traversal(nd->right, visit); 133 visit(nd); 134 } 135 136 void inorder_traversal(node *nd); 137 138 void binary_node_destroy(node * nd) { 139 if (!nd)return; 140 nd->left = 0; 141 nd->right = 0; 142 nd->parent = 0; 143 free(nd); 144 } 145 146 //the problem is that we can't make sure every node was built by `malloc` 147 void binary_tree_destroy(binary_tree *tree) { 148 if (!tree)return; 149 postorder_traversal(tree->root, binary_node_destroy); 150 free(tree); 151 } 152 153 #endif
1 #include "binary_tree.h" 2 #include <stdio.h> 3 #include <stdlib.h> 4 5 typedef int VALUE_TYPE; 6 7 int node_cmp(void *n1, void *n2) { 8 VALUE_TYPE v1 = *(VALUE_TYPE *) n1; 9 VALUE_TYPE v2 = *(VALUE_TYPE *) n2; 10 11 return v1 == v2 ? 0 : (v1 > v2 ? 1 : -1); 12 } 13 14 void visit(node * nd) { 15 printf("%d\n", *(int) nd->value); 16 } 17 18 int main() { 19 VALUE_TYPE n1[10]; 20 binary_tree *tree = binary_tree_create(); 21 assert(tree); 22 int i = 0; 23 for (; i < 10; ++i) { 24 n1[i] = rand(); 25 binary_tree_insert(tree, n1 + i); 26 } 27 inorder_traversal(tree); 28 29 return 0; 30 }