二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的 二叉树: 二叉搜索树作为一种经典的数据结构,它既有链表的快速插入与删除操作的特点,又有数组快速查找的优势,所以应用十分广泛,例如在文件系统和数据库系统一般会采用这种数据结构进行高效率的排序与检索操作。
1.基本思想
- 若任意结点的左子树不空,则左子树上所有结点的值均小于它的根结点的值
- 若任意结点的右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值
- 任意结点的左、右子树也分别为二叉查找树
满足如上条件二叉树示意图:
既然搜索二叉树满足这样一个特性,如果遍历的时候遵循中序遍历方式,那么打印出来的数据是从小达到排列的!
2.算法步骤
3.代码实现
struct BSTnode
{
int data;
struct BSTnode* lchild;
struct BSTnode* rchild;
};
二叉搜索树每一个节点的物理存储结构!
3.2.算法实现
3.3.1.创建
二叉搜索树创建的实现是很简单的,就是初始化树的根节点.
struct BSTnode* root;//创建根节点
root = NULL;//默认root为NULL
创建好根节点之后记得赋值为0.
3.3.2.查找
int binaryTreeSearch(struct BSTnode* root, int data)
{
if (root == NULL)
return 0; //return 0
else
{
if (root->data == data)
return 1;
else
{
if (root->data < data)
return binaryTreeSearch(root->rchild, data);
else
return binaryTreeSearch(root->lchild, data);
}
}
}
3.3.3.插入
struct BSTnode* binaryTreeInsert(struct BSTnode* root, int data)//插入二叉搜索树的节点
{
if (root == NULL)//找到直接返回
{
struct BSTnode* s;
//构造要插入的节点
s = (struct BSTnode*)malloc(sizeof(struct BSTnode));
s->data = data;
s->lchild = NULL;
s->rchild = NULL;
root = s;
return root;
}
else
{
if (data > root->data)
root->rchild = binaryTreeInsert(root->rchild,data);//回朔的时候将父节点与本节点联系起来
else
root->lchild = binaryTreeInsert(root->lchild,data);//回朔的时候将父节点与本节点联系起来
}
return root;//回溯的时候有用
}
3.2.4.迭代
大家需要注意,迭代的方式一定要是中序遍历,因为这个是由二叉搜索树的性质决定的。如果以中序遍历的方式遍历的话,那么遍历打印出来的数据是从小到大排列的。如果出现不满足升序的情况,那么就意味着二叉搜索树的排列是正确的!
void binaryTreeIteration(struct BSTnode* root)//中序遍历
{
if (root == NULL)
return;
binaryTreeIteration(root->lchild);
printf("%d ", root->data);
binaryTreeIteration(root->rchild);
}
3.2.5.删除
struct BSTnode* BinaryTreeInsertBp(struct BSTnode* root, struct BSTnode* child)//插入二叉搜索树的节点
{
if (child == NULL)//child为NULL意味着该处没有节点
{
return;
}
if (root == NULL)//找到直接返回
{
root = child;
return root;
}
else
{
if (child->data>root->data)
root->rchild = BinaryTreeInsertBp(root->rchild, child);//回朔的时候将父节点与本节点联系起来
else
root->lchild = BinaryTreeInsertBp(root->lchild, child);//回朔的时候将父节点与本节点联系起来
}
return root;
}
struct BSTnode* binaryTreeDelete(struct BSTnode** rootParent, int data)
{
static struct BSTnode* Parent;
static struct BSTnode* child;
struct BSTnode* root = *rootParent;
if (root == NULL)
return 0; // 终止条件,没找到待删除的元素,不进行任何操作!
else
{
if (root->data == data)//已经找到了数据,将相应的节点删除
{
if (Parent == NULL)//这就意味着要删除的是根节点,
{
printf("祖宗都敢删?吃了什么熊心豹子胆\n");
Parent = root->rchild;
if (Parent == NULL)
Parent = root->lchild;
else
BinaryTreeInsertBp(root->rchild, root->lchild);//如果你连祖宗都不放在眼里,那就uncomment
free(root);//一定要注意,free只是把root所指向的内存块给回收了,并不意味着root的值为0了
*rootParent= Parent;//这个时候要用Parent更新root的值
Parent = NULL;
return 1;
}
else//这意味着要删除的不是根节点
{
if (Parent->lchild == child)//1.检查child是parent的左孩子还是右孩子
{
Parent->lchild = child->rchild;
if (Parent->lchild == NULL)
Parent->lchild = child->lchild;
else
BinaryTreeInsertBp(Parent->lchild, child->lchild);
}
else
{
Parent->rchild = child->rchild;
if (Parent->rchild == NULL)
Parent->rchild = child->lchild;
else
BinaryTreeInsertBp(Parent->rchild, child->lchild);
}
free(child);//删除节点
//记得更新节点,清除标志
Parent = NULL;
child = NULL;
return 1;
}
}
else //还没有找到
{
Parent = root;
if (root->data < data)
{
child = root->rchild;
return binaryTreeDelete(&(root->rchild), data);
}
else
{
child = root->lchild;
return binaryTreeDelete(&(root->lchild), data);
}
}
}
}
3.2.测试程序
void main()
{
struct BSTnode* root;//创建根节点
int data, i;
int tree[] = {23,12,3,10,9,7,15,16,20,18,13,5,8,22,26,34,15,67,89,42,62,14,53};//创建树
root = NULL;//默认root为NULL
//构建搜索二叉树
for (i = 0; i < sizeof(tree)/sizeof(*tree); i++)
{
root = binaryTreeInsert(root, tree[i]);
//printf("第%d次\n",i + 1);
//tree_scaner(root);//遍历二叉搜索树
//printf("\n");
}
binaryTreeIteration(root);//遍历二叉搜索树
//在二叉搜索树种查找某个节点,并删除某个节点
while (1)
{
printf("\ninput key:");
scanf("%d", &data);
if (binaryTreeSearch(root, data))
{
printf("yes\n");
}
else
{
printf("failed\n");
}
printf("delete key:");
scanf("%d", &data);
binaryTreeDelete(&root, data);//注意,这里传递的参数是root的地址值
binaryTreeIteration(root);
}
system("pause");
}
4.实验结果与心得
在二叉搜索树的算法实现中,最重要也是最难的就是元素的删除。大家一定要多花点时间看看,这个看懂可能对提升你的代码能力有很大的帮助
本算法中有一个缺点,就是在删除的时候,如果待删除的元素不在搜索树中,不会进行相应的提示,直接忽略。但这不重要,你关注的应该是算法的设计思想!
从上面的图中,我们可以发现,数据元素是升序排列的,这是因为我选择的遍历算法是中序遍历算法。因为使用这种遍历算法能够在命令行的状态下进行看出该二叉树的分布是不是正确的(如果不是升序那就意味着树中某些部分不满足二叉搜索树的定义)
而且二叉搜索树既有数组的优点又有链表的优点.因此二叉树广泛的运用在数据库等对数据查找要求较高的领域。
完整代码
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
struct BSTnode
{
int data;
struct BSTnode* lchild;
struct BSTnode* rchild;
};
struct BSTnode* binaryTreeInsert(struct BSTnode* root, int data)//插入二叉搜索树的节点
{
if (root == NULL)//找到直接返回
{
struct BSTnode* s;
//构造要插入的节点
s = (struct BSTnode*)malloc(sizeof(struct BSTnode));
s->data = data;
s->lchild = NULL;
s->rchild = NULL;
root = s;
return root;
}
else
{
if (data > root->data)
root->rchild = binaryTreeInsert(root->rchild,data);//回朔的时候将父节点与本节点联系起来
else
root->lchild = binaryTreeInsert(root->lchild,data);
}
return root;
}
void binaryTreeIteration(struct BSTnode* root)//中序遍历
{
if (root == NULL)
return;
binaryTreeIteration(root->lchild);
printf("%d ", root->data);
binaryTreeIteration(root->rchild);
}
int binaryTreeSearch(struct BSTnode* root, int data)
{
if (root == NULL)
return 0; //return -1程序出错,if语句认为-1不是FALSE。而0是FALSE。
else
{
if (root->data == data)
return 1;
else
{
if (root->data < data)
return binaryTreeSearch(root->rchild, data);
else
return binaryTreeSearch(root->lchild, data);
}
}
}
struct BSTnode* BinaryTreeInsertBp(struct BSTnode* root, struct BSTnode* child)//插入二叉搜索树的节点
{
if (child == NULL)//child为NULL意味着该处没有节点
{
return;
}
if (root == NULL)//找到直接返回
{
root = child;
return root;
}
else
{
if (child->data>root->data)
root->rchild = BinaryTreeInsertBp(root->rchild, child);//回朔的时候将父节点与本节点联系起来
else
root->lchild = BinaryTreeInsertBp(root->lchild, child);//回朔的时候将父节点与本节点联系起来
}
return root;
}
struct BSTnode* binaryTreeDelete(struct BSTnode** rootParent, int data)
{
static struct BSTnode* Parent;
static struct BSTnode* child;
struct BSTnode* root = *rootParent;
if (root == NULL)
return 0; // 终止条件,表示没有找到带查找的数据
else
{
if (root->data == data)//已经找到了数据,将相应的节点删除
{
if (Parent == NULL)//这就意味着要删除的是根节点,
{
printf("祖宗都敢删?吃了什么熊心豹子胆\n");
Parent = root->rchild;
if (Parent == NULL)
Parent = root->lchild;
else
BinaryTreeInsertBp(root->rchild, root->lchild);//如果你连祖宗都不放在眼里,那就uncomment
free(root);//一定要注意,free只是把root所指向的内存块给回收了,并不意味着root的值为0了
*rootParent= Parent;//这个时候要用Parent更新root的值
Parent = NULL;
return 1;
}
else//这意味着要删除的不是根节点
{
if (Parent->lchild == child)//1.检查child是parent的左孩子还是右孩子
{
Parent->lchild = child->rchild;
if (Parent->lchild == NULL)
Parent->lchild = child->lchild;
else
BinaryTreeInsertBp(Parent->lchild, child->lchild);
}
else
{
Parent->rchild = child->rchild;
if (Parent->rchild == NULL)
Parent->rchild = child->lchild;
else
BinaryTreeInsertBp(Parent->rchild, child->lchild);
}
free(child);//删除节点
//记得更新节点,清除标志
Parent = NULL;
child = NULL;
return 1;
}
}
else //还没有找到
{
Parent = root;
if (root->data < data)
{
child = root->rchild;
return binaryTreeDelete(&(root->rchild), data);
}
else
{
child = root->lchild;
return binaryTreeDelete(&(root->lchild), data);
}
}
}
}
void main()
{
struct BSTnode* root;//创建根节点
int data, i;
int tree[] = {23,12,3,10,9,7,15,16,20,18,13,5,8,22,26,34,15,67,89,42,62,14,53};//创建树
root = NULL;//默认root为NULL
//构建搜索2叉树
for (i = 0; i < sizeof(tree)/sizeof(*tree); i++)
{
root = binaryTreeInsert(root, tree[i]);
//printf("第%d次\n",i + 1);
//tree_scaner(root);//遍历二叉搜索树
//printf("\n");
}
binaryTreeIteration(root);//遍历二叉搜索树
//在二叉搜索树种查找某个节点
while (1)
{
printf("\ninput key:");
scanf("%d", &data);
if (binaryTreeSearch(root, data))
{
printf("yes\n");
}
else
{
printf("failed\n");
}
printf("delete key:");
scanf("%d", &data);
binaryTreeDelete(&root, data);
binaryTreeIteration(root);
}
system("pause");
}