引言
二叉搜索树(Binary Search Tree
),也称BST树、二叉搜索树、有序二叉树(ordered binary tree
),排序二叉树(sorted binary tree
).
它是指一棵空树或者具有下列性质的二叉树:
1. 任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
2. 任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
3. 任意节点的左、右子树也分别为二叉查找树;
4. 没有键值相等的节点。
一. 二叉搜索树定义
二叉搜索树,我们将二叉树节点的类型定义为:
typedef struct Tree_node Tree_node;
struct Tree_node //BST树节点
{
int data; //我们以int做测试
Tree_node * left_child; //左孩子
Tree_node * right_child; //右孩子
};
typedef Tree_node BSTree; //BST树
二.二叉树搜索的接口
- bstree.h
#ifndef _BINARY_SEARCH_TREE_H_
#define _BINARY_SEARCH_TREE_H_
#include "tools.h"
typedef struct Tree_node Tree_node;
struct Tree_node
{
int data;
Tree_node * left_child;
Tree_node * right_child;
};
typedef Tree_node BSTree;
//1.销毁BST二叉搜索树
void BSTree_destroy(BSTree **root);
//2.中序遍历BST二叉搜索树
void mid_order_print(BSTree *root);
//3.寻找BST二叉搜索树中的最大数
Tree_node *BSTree_max(BSTree *root);
//4.寻找BST二叉搜索树中的最小数
Tree_node *BSTree_min(BSTree *root);
//5.通过数组创建BST二叉搜索树
BSTree *BSTree_create(int *a, int length);
//6.删除BST中某节点
Boolean BSTree_delete(BSTree *root, int p);
//7.1.在BST二叉搜索树中插入某节点
Boolean BSTree_insert0(BSTree **root, int p);
//7.2.在BST二叉搜索树中插入某节点
Boolean BSTree_insert1(BSTree **root, int p);
//8.在BST二叉搜索树中根据值寻找某节点
Tree_node *BSTree_search(BSTree *root, int p);
//9.在BST二叉搜索树中寻找某节点的父节点
Tree_node *find_parent(BSTree *root, Tree_node *node);
#endif//_BINARY_SEARCH_TREE_H_
三. 搜索二叉树的接口实现
- bstree.c
#include "bstree.h"
#include "tools.h"
//创建节点
static Tree_node *create_node(void)
{
Tree_node *node = (Tree_node *)Malloc(sizeof(Tree_node));
bzero(node, sizeof(Tree_node));
return node;
}
//1.销毁BST二叉搜索树
void BSTree_destroy(BSTree **root)
{
if(*root != NULL)
{
BSTree_destroy(&(*root)->left_child); //先处理左孩子
BSTree_destroy(&(*root)->right_child); //先处理右孩子
free(*root); //销毁当前节点
*root = NULL;
}
}
//2.中序遍历BST二叉搜索树
//根据二叉排序树的性质,中序遍历得到升序序列
void mid_order_print(BSTree *root)
{
if(root != NULL){
mid_order_print(root->left_child);
printf("%d ", root->data);
mid_order_print(root->right_child);
}
}
//3.寻找BST二叉搜索树中的最大数
//由于是二叉搜索树,所以,最大值在右孩子的最右边节点
Tree_node *BSTree_max(BSTree *root)
{
if(NULL == root)
{
return NULL;
}
if(NULL == root->right_child) //若无右孩子,当前根节点比左子树都大
{
return root;
}
else
{
return BSTree_max(root->right_child);//递归右边查找
}
}
//4.寻找BST二叉搜索树中的最小数
//最小值在最左节点
Tree_node *BSTree_min(BSTree *root)
{
if(NULL == root)
{
return NULL;
}
if(NULL == root->left_child)
{
return root;
}
else
{
return BSTree_min(root->left_child);
}
}
//5.通过数组创建BST二叉搜索树
//
//
// Φ 8 8 8 8 8 8 8 8 8
// / / \ / \ / \ / \ / \ / \ / \
// 3 3 10 3 10 3 10 3 10 3 10 3 10 3 10
// / / \ / \ \ / \ \ / \ \ / \ \
// 1 1 6 1 6 14 1 6 14 1 6 14 1 6 14
// / / \ / \ /
// 4 4 7 4 7 13
BSTree *BSTree_create(int *a, int length)
{
int i = 0;
BSTree *root = NULL;
if(NULL == a || length < 0)
{
return NULL;
}
for(i = 0; i < length; ++i)
{
printf("a[%d],%d\n", i, a[i]);
BSTree_insert1(&root, a[i]); //依次插入数组中元素
}
return root;
}
//6.删除BST中某节点
// 1. 欲删除节点为叶子节点,如1,则释放掉并将其父节点指向其节点的指针置为空;
// 2. 欲删除节点无左孩子有右孩子,如10,则用其右孩子的左右子树替换当前左右子树,并释放;
// 3. 欲删除节点无右孩子有左孩子,如14,则用其左孩子的左右子树替换当前左右子树,并将其释放.
// 4. 欲删除节点左右孩子均不为空,如8,在删去*p之后,为保持其它元素之间的相对位置不变,可按中序遍历保持有序进行调整。
// 比较好的做法是,找到*p的直接前驱(或直接后继)*s,用*s来替换结点*p,然后再删除结点*s。
Boolean BSTree_delete(BSTree *root, int key)
{
Tree_node *p = NULL;
Tree_node *q = NULL;
Tree_node *s = NULL;
if(NULL == root)
{
return FALSE;
}
if(NULL == (p = BSTree_search(root, key)))
{
return FALSE;
}
if(NULL == p->left_child && NULL == p->right_child)//叶子节点
{
q = p;
s = find_parent(root, BSTree_search(root, key));
if(s->left_child->data == q->data) //父节点指向空
s->left_child = NULL;
else
s->right_child = NULL;
free(q);
}
else if(NULL == p->left_child && p->right_child != NULL) //2无左子树有右子树 如10
{
q = p->right_child;
p->data = p->right_child->data;
p->left_child = q->left_child;
p->right_child = q->right_child;
free(q);
}
else if(NULL == p->right_child && p->left_child != NULL) // 3无右孩子有左孩子 如14
{
q = p->left_child;
p->data = p->left_child->data;
p->left_child = q->left_child;
p->right_child = q->right_child;
free(q);
}
else //左右子树均不为空 如8
{
q = p;
s = p->left_child; //左转
while(s->right_child)//向右走向尽头
{
q = s;
s = s->right_child;
}
p->data = s->data; //s指向被删节点的"前驱"
if(q != p)
q->right_child = s->left_child; //重接q的右子树
else
q->left_child = s->left_child; //重接q的左子树
free(s);
}
return TRUE;
}
//7.1.在BST二叉搜索树中插入某节点
Boolean BSTree_insert0(BSTree **root, int data)
{
Tree_node *p = create_node();
p->data = data;
if(NULL == *root)
{
*root = p;
return TRUE;
}
//插入当前节点的左孩子
if((*root)->left_child == NULL && (*root)->data > data)
{
(*root)->left_child = p;
return TRUE;
}
//插入当前节点的右孩子
if((*root)->right_child == NULL && (*root)->data < data)
{
(*root)->right_child = p;
return TRUE;
}
if((*root)->data > data)
{
BSTree_insert0(&((*root)->left_child), data);
}
else if((*root)->data < data)
{
BSTree_insert0(&((*root)->right_child), data);
}
else
{
return FALSE;
}
}
//7.2.在BST二叉搜索树中插入某节点
// 首先查找,BST中若有此节点,则不进行插入.
Boolean BSTree_insert1(BSTree **root, int data)
{
if(BSTree_search(*root, data) != NULL)//已存在不插入
{
return FALSE;
}
if(NULL == *root) //根为空
{
Tree_node *p = create_node();
p->data = data;
*root = p;
return TRUE;
}
else if((*root)->data > data) //在左子树
{
BSTree_insert1(&(*root)->left_child, data);
return TRUE;
}
else if((*root)->data < data)
{
BSTree_insert1(&(*root)->right_child, data);
return TRUE;
}
else
{
return FALSE;
}
}
//8.在BST二叉搜索树中根据值寻找某节点
//递归查找
Tree_node *BSTree_search(BSTree *root, int key)
{
if(NULL == root)
{
return NULL;
}
if(root->data < key)
{
return BSTree_search(root->right_child, key);
}
else if(root->data > key)
{
return BSTree_search(root->left_child, key);
}
else
{
return root;
}
}
//9.在BST二叉搜索树中寻找某节点的父节点
Tree_node *find_parent(BSTree *root, Tree_node *node) //找到指定节点的双亲节点
{
Tree_node *p_node = NULL;
if(root == NULL || root->left_child == node
|| root->right_child == node){
//root不存在或者root是所要找的双亲节点
return root;
}
p_node = find_parent(root->left_child, node);
if(p_node == NULL)
{
p_node = find_parent(root->right_child, node);
}
return p_node;
}
四. 函数功能检测
- main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "bstree.h"
#include "tools.h"
int main(int argc, char **argv)
{
BSTree *root = NULL;
Tree_node * x = NULL;
int a[]=
//{1,2,3,4,5,6};
//int a[]={3, 1, 4};
{8, 3, 10, 1, 6, 14, 4, 7, 13};
int length = sizeof(a)/sizeof(a[0]);
printf("length:%d\n", length);
root = BSTree_create(a, length);
// 8
// / \
// 3 10
// / \ \
// 1 6 14
// / \ /
// 4 7 13
//
printf("%d ", root->data);
printf("%d ", root->left_child->data);
printf("%d \n", root->right_child->data);
if(x = BSTree_search(root, 3))
{
printf("search: %d\n", x->data);
}
if(x = BSTree_search(root, 4))
{
printf("search: %d\n", x->data);
}
if(x = BSTree_search(root, 5))
{
printf("search: %d\n", x->data);
}
printf("max: %d.", BSTree_max(root)->data);
printf("min: %d.\n", BSTree_min(root)->data);
printf("parent:%d\n",find_parent(root, BSTree_search(root, 1))->data);
printf("parent:%d\n",find_parent(root, BSTree_search(root, 3))->data);
mid_order_print(root);
printf("\ndelete\n");
BSTree_delete(root, 8);
BSTree_insert1(&root, 3);
//printf("%d ", root->left_child->left_child->data);
printf("max: %d.", BSTree_max(root)->data);
printf("min: %d.\n", BSTree_min(root)->data);
mid_order_print(root);
printf("\n");
BSTree_destroy(&root);
return 0;
}
- 运行结果
root@aemonair:~/BSTree# cc.sh *.c
Compiling ...
-e CC bstree.c main.c tools.c -g -lpthread -lm
-e Completed .
-e Sat Aug 20 21:43:11 CST 2016
root@aemonair:~/BSTree# ./bstree
length:9
a[0],8
a[1],3
a[2],10
a[3],1
a[4],6
a[5],14
a[6],4
a[7],7
a[8],13
8 3 10
search: 3
search: 4
max: 14.min: 1.
parent:3
parent:8
1 3 4 6 7 8 10 13 14
delete
max: 14.min: 1.
1 3 4 6 7 10 13 14
五. 总结
二叉查找树相比于其他数据结构的优势在于查找、插入的时间复杂度较低。为O(log n)
。二叉查找树是基础性数据结构,用于构建更为抽象的数据结构,如集合、multiset、关联数组等。
二叉查找树的查找过程和次优二叉树类似,通常采取二叉链表作为二叉查找树的存储结构。中序遍历二叉查找树可得到一个关键字的有序序列,一个无序序列可以通过构造一棵二叉查找树变成一个有序序列,构造树的过程即为对无序序列进行查找的过程。每次插入的新的结点都是二叉查找树上新的叶子结点,在进行插入操作时,不必移动其它结点,只需改动某个结点的指针,由空变为非空即可。搜索、插入、删除的复杂度等于树高,期望 O(log n)
,最坏O(n)
(数列有序,树退化成线性表)。
虽然二叉查找树的最坏效率是O(n)
,但它支持动态查询,且有很多改进版的二叉查找树可以使树高为 O(log n)
,如SBT,AVL树,红黑树等。故不失为一种好的动态查找方法。
可是嘞,我们一步一步来好了~