二分查找的缺点:
当需要插入或者删除数据元素时,为了能够继续进行二分查找,需要大规模挪动有序表中的数据元素进行排序,使得插入或者删除后的线性表保持有序。因此二分查找更适合于静态的情况下进行查找。
二分查找的过程实质是一棵二叉树。
如果需要动态的情况下进行查找,可以采用构建二叉排序树的方式。
二叉排序树 Binary Sort Tree
定义:
二叉排序树是一棵空树,或者
• 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值。
• 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值。
• 它的左右子树也分别是二叉排序树。
特性:
任意一个结点的值都大于其左子树的所有结点值
任意一个结点的值都小于其右子树的所有结点值
二分查找的特性可知树结点查找的时间复杂度为O(log2 n)。
二叉排序树是特殊的二叉树,因此具有与二叉树相同的操作。
二叉排序树的插入和删除操作:
二叉排序树的所有操作都必须保证其二叉排序性不变。
插入:
其插入操作总是在叶结点处进行
删除:
• 叶结点:直接删除
• 非叶结点:查找合适的替代者后删除
有一个孩子的结点:
• 用孩子结点代替原结点
有两个孩子的结点:
• 用中序遍历下的直接前驱替换原结点,即中序遍历的队列中排在被删除节点前一位的那个节点被用来替换要删除的节点。
二叉排序树的代码实现
//二叉排序树 头文件 BSTree.h
#ifndef _BSTREE_H_
#define _BSTREE_H_
typedef void BSTree;
typedef void BSKey;
typedef struct _tag_BSTreeNode BSTreeNode;
struct _tag_BSTreeNode
{
BSKey* key;
BSTreeNode* left;
BSTreeNode* right;
};
typedef void (BSTree_Printf)(BSTreeNode*);//自定义的带打印函数类型
typedef int (BSTree_Compare)(BSKey*, BSKey*);//自定义的比较函数类型
BSTree* BSTree_Create();
void BSTree_Destroy(BSTree* tree);
void BSTree_Clear(BSTree* tree);
int BSTree_Insert(BSTree* tree, BSTreeNode* node, BSTree_Compare* compare);
BSTreeNode* BSTree_Delete(BSTree* tree, BSKey* key, BSTree_Compare* compare);
BSTreeNode* BSTree_Get(BSTree* tree, BSKey* key, BSTree_Compare* compare);
BSTreeNode* BSTree_Root(BSTree* tree);
int BSTree_Height(BSTree* tree);
int BSTree_Count(BSTree* tree);
int BSTree_Degree(BSTree* tree);
void BSTree_Display(BSTree* tree, BSTree_Printf* pFunc, int gap, char div);
#endif
//二叉排序树 源文件 BSTree.c
#include <stdio.h>
#include <malloc.h>
#include "BSTree.h"
typedef struct _tag_BSTree TBSTree;
struct _tag_BSTree
{
int count;
BSTreeNode* root;
};
static void recursive_display(BSTreeNode* node, BSTree_Printf* pFunc, int format, int gap, char div)
// O(n) 递归打印二叉排序树的各个节点的内容
{
int i = 0;
if( (node != NULL) && (pFunc != NULL) )
{
for(i=0; i<format; i++)
{
printf("%c", div);
}
pFunc(node);
printf("\n");
if( (node->left != NULL) || (node->right != NULL) )
{
recursive_display(node->left, pFunc, format + gap, gap, div);
recursive_display(node->right, pFunc, format + gap, gap, div);
}
}
else
{
for(i=0; i<format; i++)
{
printf("%c", div);
}
printf("\n");
}
}
static int recursive_count(BSTreeNode* root) // O(n)
//递归 计算二叉排序树的节点的数目
{
int ret = 0;
if( root != NULL )
{
ret = recursive_count(root->left) + 1 + recursive_count(root->right);
}
return ret;
}
static int recursive_height(BSTreeNode* root) // O(n)
//递归 计算二叉排序树的高度
{
int ret = 0;
if( root != NULL )
{
int lh = recursive_height(root->left);
int rh = recursive_height(root->right);
ret = ((lh > rh) ? lh : rh) + 1;
}
return ret;
}
static int recursive_degree(BSTreeNode* root) // O(n)
//递归 计算二叉排序树的度
{
int ret = 0;
if( root != NULL )
{
if( root->left != NULL )
{
ret++;
}
if( root->right != NULL )
{
ret++;
}
if( ret == 1 )
{
int ld = recursive_degree(root->left);
int rd = recursive_degree(root->right);
if( ret < ld )
{
ret = ld;
}
if( ret < rd )
{
ret = rd;
}
}
}
return ret;
}
static int recursive_insert(BSTreeNode* root, BSTreeNode* node, BSTree_Compare* compare)
//递归插入节点
{
int ret = 1;
int r = compare(node->key, root->key); //自定义的比较函数
if( r == 0 ) //如果节点已经存在,不用插入
{
ret = 0;
}
else if( r < 0 ) //小于父节点,插入左枝
{
if( root->left != NULL )
{
ret = recursive_insert(root->left, node, compare);
}
else
{
root->left = node;
}
}
else if( r > 0 ) //大于父节点,插入右枝
{
if( root->right != NULL )
{
ret = recursive_insert(root->right, node, compare);
}
else
{
root->right = node;
}
}
}
static BSTreeNode* recursive_get(BSTreeNode* root, BSKey* key, BSTree_Compare* compare)
//递归 获取节点的数值
{
BSTreeNode* ret = NULL;
if( root != NULL )
{
int r = compare(key, root->key);
if( r == 0 )
{
ret = root;
}
else if( r < 0 )
{
ret = recursive_get(root->left, key, compare);
}
else if( r > 0 )
{
ret = recursive_get(root->right, key, compare);
}
}
return ret;
}
static BSTreeNode* delete_node(BSTreeNode** pRoot)
// 删除节点, 先判断孩子节点的数目,一个孩子节点,孩子替代父亲;
// 两个孩子节点,中序遍历的前驱替换被删除的节点
{
BSTreeNode* ret = *pRoot;
if( (*pRoot)->right == NULL )//一个孩子节点的情况
{
*pRoot = (*pRoot)->left;
}
else if( (*pRoot)->left == NULL )
{
*pRoot = (*pRoot)->right;
}
else //两个节点的情况
{
BSTreeNode* g = *pRoot;//当前节点
BSTreeNode* c = (*pRoot)->left;//当前节点的左孩子
while( c->right != NULL ) //找到中序遍历中 *pRoot 的前驱 c
{
g = c;
c = c->right;
}
if( g != *pRoot )
{
g->right = c->left;
//如果g不指向要删除的节点,c的左枝进到c的位置
//然后,c来代替要删除的节点
}
else
{
g->left = c->left;
//如果g指向要删除的节点,也就是c的right为NULL
//由c点的左枝代替要删除节点的左枝
}
c->left = (*pRoot)->left;
c->right = (*pRoot)->right;
*pRoot = c; //由c点代替要删除的节点
}
return ret;
}
static BSTreeNode* recursive_delete(BSTreeNode** pRoot, BSKey* key, BSTree_Compare* compare)
//递归 删除节点
{
BSTreeNode* ret = NULL;
if( (pRoot != NULL) && (*pRoot != NULL) )
{
int r = compare(key, (*pRoot)->key);//比较 当前节点与要删除节点的值
if( r == 0 )//如果等于,则删除该节点
{
ret = delete_node(pRoot);
}
else if( r < 0 )//如果不等于,则继续向分支递归
{
ret = recursive_delete(&((*pRoot)->left), key, compare);
}
else if( r > 0 )
{
ret = recursive_delete(&((*pRoot)->right), key, compare);
}
}
return ret;
}
BSTree* BSTree_Create() // O(1) 创建 二叉排序树
{
TBSTree* ret = (TBSTree*)malloc(sizeof(TBSTree));
if( ret != NULL )
{
ret->count = 0;
ret->root = NULL;
}
return ret;
}
void BSTree_Destroy(BSTree* tree) // O(1) 销毁 二叉排序树
{
free(tree);
}
void BSTree_Clear(BSTree* tree) // O(1) 清空 二叉排序树
{
TBSTree* btree = (TBSTree*)tree;
if( btree != NULL )
{
btree->count = 0;
btree->root = NULL;
}
}
int BSTree_Insert(BSTree* tree, BSTreeNode* node, BSTree_Compare* compare)
//向二叉排序树中插入节点,compare是自定义的比较函数
{
TBSTree* btree = (TBSTree*)tree;
int ret = (btree != NULL) && (node != NULL) && (compare != NULL);
if( ret )
{
node->left = NULL;
node->right = NULL;
if( btree->root == NULL )//如果是空树,则插入在根节点处
{
btree->root = node;
}
else
{
ret = recursive_insert(btree->root, node, compare);
//递归插入节点
}
if( ret ) //如果插入成功
{
btree->count++;
}
}
return ret;
}
BSTreeNode* BSTree_Delete(BSTree* tree, BSKey* key, BSTree_Compare* compare)
//删除其中一个节点,当有两个孩子时,中序遍历的前驱来替换
{
TBSTree* btree = (TBSTree*)tree;
BSTreeNode* ret = NULL;
if( (btree != NULL) && (key != NULL) && (compare != NULL) )
{
ret = recursive_delete(&btree->root, key, compare);
if( ret != NULL )
{
btree->count--;
}
}
return ret;
}
BSTreeNode* BSTree_Get(BSTree* tree, BSKey* key, BSTree_Compare* compare)
//获取一个节点
{
TBSTree* btree = (TBSTree*)tree;
BSTreeNode* ret = NULL;
if( (btree != NULL) && (key != NULL) && (compare != NULL) )
{
ret = recursive_get(btree->root, key, compare);
}
return ret;
}
BSTreeNode* BSTree_Root(BSTree* tree) // O(1)
{
TBSTree* btree = (TBSTree*)tree;
BSTreeNode* ret = NULL;
if( btree != NULL )
{
ret = btree->root;
}
return ret;
}
int BSTree_Height(BSTree* tree) // O(n)
{
TBSTree* btree = (TBSTree*)tree;
int ret = 0;
if( btree != NULL )
{
ret = recursive_height(btree->root);
}
return ret;
}
int BSTree_Count(BSTree* tree) // O(1)
{
TBSTree* btree = (TBSTree*)tree;
int ret = 0;
if( btree != NULL )
{
ret = btree->count;
}
return ret;
}
int BSTree_Degree(BSTree* tree) // O(n)
{
TBSTree* btree = (TBSTree*)tree;
int ret = 0;
if( btree != NULL )
{
ret = recursive_degree(btree->root);
}
return ret;
}
void BSTree_Display(BSTree* tree, BSTree_Printf* pFunc, int gap, char div) // O(n)
{
TBSTree* btree = (TBSTree*)tree;
if( btree != NULL )
{
recursive_display(btree->root, pFunc, 0, gap, div);
}
}
//二叉排序树 main文件 main.c
#include <stdio.h>
#include <stdlib.h>
#include "BSTree.h"
struct Node
{
BSTreeNode header;
char v;
};
void printf_data(BSTreeNode* node) //自定义的打印函数
{
if( node != NULL )
{
printf("%c", ((struct Node*)node)->v);
}
}
int compare_key(BSKey* k1, BSKey* k2)//自定义的比较函数
{
return (int)k1 - (int)k2;
}
int main(int argc, char *argv[])
{
BSTree* tree = BSTree_Create();
struct Node n1 = {{(BSKey*)1, NULL, NULL}, 'A'};
struct Node n2 = {{(BSKey*)2, NULL, NULL}, 'B'};
struct Node n3 = {{(BSKey*)3, NULL, NULL}, 'C'};
struct Node n4 = {{(BSKey*)4, NULL, NULL}, 'D'};
struct Node n5 = {{(BSKey*)5, NULL, NULL}, 'E'};
struct Node n6 = {{(BSKey*)6, NULL, NULL}, 'F'};
BSTree_Insert(tree, (BSTreeNode*)&n4, compare_key);
BSTree_Insert(tree, (BSTreeNode*)&n1, compare_key);
BSTree_Insert(tree, (BSTreeNode*)&n3, compare_key);
BSTree_Insert(tree, (BSTreeNode*)&n6, compare_key);
BSTree_Insert(tree, (BSTreeNode*)&n2, compare_key);
BSTree_Insert(tree, (BSTreeNode*)&n5, compare_key);
printf("Full Tree: \n");
BSTree_Display(tree, printf_data, 4, '-');
printf("\n");
printf("Height: %d\n", BSTree_Height(tree));
printf("Degree: %d\n", BSTree_Degree(tree));
printf("Count: %d\n", BSTree_Count(tree));
printf("Search Key 5: %c\n", ((struct Node*)BSTree_Get(tree, (BSKey*)5, compare_key))->v);
printf("\n");
BSTree_Delete(tree, (BSKey*)4, compare_key);
printf("After Delete Key 4: \n");
printf("Height: %d\n", BSTree_Height(tree));
printf("Degree: %d\n", BSTree_Degree(tree));
printf("Count: %d\n", BSTree_Count(tree));
printf("Full Tree: \n");
BSTree_Display(tree, printf_data, 4, '-');
printf("\n");
BSTree_Clear(tree);
printf("After Clear: \n");
printf("Height: %d\n", BSTree_Height(tree));
printf("Degree: %d\n", BSTree_Degree(tree));
printf("Count: %d\n", BSTree_Count(tree));
BSTree_Display(tree, printf_data, 4, '-');
BSTree_Destroy(tree);
return 0;
}
小结
二分查找仅适用于静态查找的情形,而二叉排序树适合于动态查找的情形。
二叉排序树是从二分查找的过程总结而来的一种数据结构。
二叉排序树的查找效率和二分查找相同。
二叉排序树是特殊的二叉树其插入和删除操作必须保证其二叉排序的性质不变。