二叉搜索树
树的递归定义:
树是一种数据结构,它要么为空,要么具有一个值并具有零个或多个孩子,每个孩子本身也是一个树。
二叉树是树的特殊形式,它的每个节点至多有两个孩子,分别称作左孩子和右孩子。
二叉搜索树还具有一个额外的性质:每个节点的值比它的左子树的所有节点的值都要大,但比它的右子树的所有节点的值都要小。
注意:这个定义排除了树中存在值相同的节点可能
这使得二叉搜索树成为一种用关键值快速查找的工具,它的查找时间复杂度可为O(log n),因为它是用到二分法快速锁定关键值。
(二叉搜索树)
在二叉搜索树的插入
当一个新值添加到一颗二叉搜索树时,它必须被放在合适的位置,继续保持二叉搜索树的属性。
插入的基本算法:
如果树为空:
把新值作为根节点插入
否则:
如果新值小于当前节点的值:
把新值插入到当前节点的左子树中
否则:
把新值插入到当前节点的右子树中
上述算法的递归表达正是对树的递归定义的直接结果。
( 注意:由于上述递归在算法的尾部出现<尾部递归>,所以我们可以用到迭代跟有效的实现这个算法 )
从二叉搜索树删除节点
从树的中部删除一个节点将会导致节点的子树和树的其余部分分离,我们必须重新连接它们。
所以从二叉搜索树删除节点分三种情况:
- 删除节点为叶节点,则直接删除叶节点不会导致任何子树断开,所以不存在重新连接问题。
- 删除节点只有一个孩子,则删除节点只需要将删除节点的父节点和它的孩子节点连接起来即可。
- 删除节点具有两个孩子,则其中一种策略是找到该节点左子树的最大值,并将最大值节点删除,同时用这个最大值代替原先要删除那个节点的值。
在二叉搜索树中查找
根据二叉搜索树本身特性,则在树中查找值并不困难:
如果树为空:
这个值不在树中
否则:
如果这个值和根节点的值相同:
成功找到这个值
否则:
如果这个值小于根节点的值:
查找左子树
否则:
查找右子树
这个递归算法也属于尾部递归,所以采用迭代的方案更具效率。
在二叉搜索树中遍历
当你在检查这棵树的所有节点时,就是在遍历这棵树。
遍历树的节点有几种不同的遍历方式:前序,中序,后序,以及层次遍历。
上图所描述的树的前序遍历为:20,12,5,16,25,28 (中->左->右)
中序遍历为:5,12,16,20,25,28(左->中->右)
后序遍历为:5,16,12,28,25,10(左->右->中)
层次遍历为:20,12,25,5,16,28
二叉搜索树的接口文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
|
#define TREE_TYPE int
typedef struct TREE_NODE { TREE_TYPE value; struct TREE_NODE *left; struct TREE_NODE *right; } TreeNode;
void insert(TREE_TYPE value);
TREE_TYPE *find(TREE_TYPE value);
TREE_TYPE max_tree(TreeNode *link);
void remove_value(TREE_TYPE value);
void pre_order_traverse(void (*callback)(TREE_TYPE value));
|
tree.h文件
二叉搜索树的线性结构
数组形式的二叉搜索树
用数组形式去实现二叉搜索树的关键是使用下标寻找节点的父节点和孩子节点。
下面有两套映射规则:
基于根节点为节点1,且数组从1开始的映射规则
节点N的父节点为节点N/2
节点N的左孩子节点为节点2N
节点N的右孩子节点为节点2N+1
基于根节点为节点0,且数组从0开始的映射规则
节点N的父节点为节点(N+1)/2 - 1
节点N的左孩子节点为节点2N+1
节点N的右孩子节点为节点2N+2
上述两套规则都可以实现用数组来表示二叉搜索树
第一套可读性高,且符合现实习惯,但数组下标从1开始忽略了数组第一个元素的空间,在面对树中储存元素较大时浪费了空间。
在用数组实现二叉搜索树时,如何对未使用的数组元素初始化来表示该节点未使用也未赋值,用0表示也是一种方式,但0也是一个合法的数据值,这个时候就可以使用一个比较数组,它的元素是布尔类型,用于提示哪个节点被使用。
缺陷:无论是静态数组,还是动态数组来实现二叉搜索树,都会存在一个问题,那就是面对不是那么平衡树来说,往往会浪费很大的空间,而且每次的新插入会使数组大小扩大一倍,这样使可用空间迅速耗尽。
二叉搜索树的链式结构
最为常见的二叉搜索树实现方式,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
#include "tree.h" #include <assert.h> #include <stdio.h> #include <malloc.h>
static TreeNode *tree;
void insert(TREE_TYPE value){ TreeNode *current; TreeNode **link = &tree;
while ((current = *link) != NULL) { if(value < current->value) link = ¤t->left; else { assert(value != current->value); link = ¤t->right; } }
current = malloc(sizeof(TreeNode)); assert(current != NULL); current->value = value; current->left = NULL; current->right = NULL; *link = current; }
TREE_TYPE *find(TREE_TYPE value){ TreeNode *current = tree;
while (current != NULL && current->value != value){ if(value < current->value) current = current->left; else current = current->right; }
if(current != NULL) return ¤t->value; else return NULL; }
TREE_TYPE max_tree(TreeNode *link){ while (link->right != NULL) link = link->right; return link->value; }
void remove_value(TREE_TYPE value){ TreeNode *current; TreeNode **link = &tree;
while ((current = *link)->value != value) { if (value < current->value) link = ¤t->left; else { assert(value != current->value); link = ¤t->right; } }
if (current->left == NULL && current->right == NULL) *link = NULL; else if (current->left == NULL || current->right == NULL) { if (current->left == NULL) *link = current->right; else *link = current->left; } else { TREE_TYPE max = max_tree(current->left); remove_value(max); current->value = max; } }
static void do_pre_order_traverse(TreeNode *current, void (*callback)(TREE_TYPE value)){ if (current != NULL){ callback(current->value); do_pre_order_traverse(current->left, callback); do_pre_order_traverse(current->right, callback); } }
void pro_order_traverse(void (*callback)(TREE_TYPE value)){ do_pre_order_traverse(tree, callback); }
void test_printf(TREE_TYPE value){ printf("%d",value); } int main(){ insert(2); insert(1); insert(3); remove_value(3); TREE_TYPE *k = find(3); if(k == NULL) printf("not find\n"); else printf("find %d\n", *k); pro_order_traverse(test_printf); printf("\n"); insert(4); insert(0); insert(9); printf("tree maximum %d\n",max_tree(tree)); pro_order_traverse(test_printf); }
|
link_tree.c文件
测试输出: