一直对于二叉搜索树(又叫二叉排序树,也叫二叉查找树),没有很好的理解,决定花点时间来学习and总结。。
二叉搜索树也是二叉树的一种。(就像堆也就二叉树的一种一样。。。)
只不过,二叉搜索树也是有其他要求:对于所有的子树,其根节点的值大于左子树上的所有结点的值,而小于右子树上所有结点的值的值。。
对于错误的理解:对于所有的结点,要大于其左结点,小于其右结点。。(PS:这种理解是错误的,一定要注意。。)
还有一点需要注意的是:我们的大于和小于都应该是严格的。
========== 下面主要针对,其建立、增删改查这些操作来进行说明 ==============
====== 对于所有的数据结构,我们对其增删改查都应该有比较清楚的认识==========
首先我们定义一下二叉树的结构(链式结构):
<span style="font-size:18px;"><span style="font-size:18px;"># 结点的数据结构
typedef int ElementType;
typedef struct
{
ElementType Element;
ElementType *left;
ElementType *right;
}TreeNode;
# 有必要区别一下结点和树的区别。
具体来说的话:
结点的话,就是有数据+空间。
树的话,就是一个指向树的头结点的指针。
现在,我说给你你棵树,肯定就是给你一个指向头结点的指针。
</span></span>
MakeEmpty:
所谓makeempty就是一个用来初始化为一个空的二叉搜索树。(注意这个‘为’字 = =)
这里是初始化,我们理解为:现在给你一棵树(可能不是一棵空树),我们如何来初始化。
========== 插一腿 =========
有必要解释一下,新建和初始化,我对这两个概念理解上的区别。
比如:
现在我要新建一棵二叉树:
新建二叉树的话,就是要新建一个的root结点(或者说新建一个指向root结点的指针)。
现在我要初始化一棵二叉树:
初始化二叉树的话,就是现在一棵二叉树已经建好了,我要初始化他。在没有插入任何一个结点的时候,初始化就显的比较尴尬了。不用分配空间,不用初始化数据。
ppps:对于这两个概念的区别还是有待更加深刻的理解(特别是树)。有待以后学习。
对于这个 问题,我又了解了一些情况。这儿有一个帖子,讨论的比较有>>>>>>http://bbs.youkuaiyun.com/topics/360102904
加上自己的一点理解:创建的话,你只需要指定,你现在已经创建了一个你的数据结构;而初始化的话,就是你需要对你的数据进行处理。。。
比如现在你要创建和初始化一棵树(无论是什么样的数):
创建一棵树,就是你要个给出这棵树的一个接口(头结点)。想一想,对于给你一棵树,你会得到什么?就是该树得头结点啊。。
那么对于初始化呢?初始化一棵树的话,我们就需要对说有的结点进行空间的分配和数据的存储。。
现在对于新建和初始化应该有很清楚的理解。。。
=========================
Code:
<span style="font-size:18px;"><span style="font-size:18px;">返回的是一棵树。而不是。。。。
TreeNode* MakeEmpty(TreeNode* tree)
{
if(tree != NULL){
MakeEmpty(tree -> left);
MakeEmpty(tree -> right);
free(tree);
}
return NULL;
}
</span></span>
Find, FindMin and FindMax:
对于查找来说,我们的二叉搜索树还是有一定的优势的。(这也是为什么我们的二叉搜索树也叫二叉查找树)。
对于所有类型的查找(无论是找什么样元素,存在的,不存在的,最大的,最小的),我们的平均查找时间为log(n),n为该树中的所有结点。
<span style="font-size:18px;">// 返回值为结点的地址。
// 寻找值为x的结点。。
TreeNode* Find(ElementType x, TreeNode* T)
{
if(T == NULL) return NULL;
if(T -> Element > x ){
return Find(x, TreeNode -> left);
}
else if(T -> Element < x) {
return Find(x, TreeNode -> right);
}
return T;
}
// 寻找最小值的结点。。
TreeNode* FindMin(TreeNode* T)
{
if(T == NULL) return NULL;
if(T -> left == NULL){
return T;
}
return FindMin(T -> left);
}
// 寻找最大值的结点。。
TreeNode* FindMax(TreeNode* T)
{
if(T == NULL) return NULL;
if(T -> right == NULL) {
return T;
}
return FindMax(T -> right);
}
</span>
对于二叉搜索树的查找操作还是比较简单的。。。
对于他的递归写法很是简单;当然,对于其的循环写法就更加的简单了。。。
Insert:
插入的话,我们最优(或者说,最简单)的做法就是,把需要插入的结点插入为叶子结点。
时间复杂度的话,就是logn。
<span style="font-size:18px;">// 插入
TreeNode* Insert(ElementType x, TreeNode* T)
{
if(T == NULL){
T = (TreeNode*)malloc(sizeof(TreeNode));
T -> Element = x;
T -> right = T -> left = NULL;
}
if(x > T -> Element){
T -> right = Insert(x, T -> right);
}
else if(x < T -> Element){
T -> left = Insert(x, left);
}
return T; // 不要忘了返回插入以后的二叉树。
}</span>
Delete:
对于二叉搜索树的删除, 是所有操作中最难的一个。(这和其他的数据结构一样,最难的操作就是删除。)
删除的情况有很多情况:
1,删除的是叶子结点:
最简单的应该就是删除的叶子结点了。删除叶子对于其他的结点没有任何的联系,所以,直接删除就可以了。
2,删除的结点只有一个棵子树:
对于只有一棵只树的结点,也是比较简单的,我们直接把该结点的唯一子树把该结点替换了就可以了。。
3,删除的结点有两棵子树:
最麻烦的应该就是删除这种类型的结点了,那么我们具体应该怎么做呢?我们这样想,我们删除这个结点以后应该用谁来代替他的位置,以至于仍然还是棵二叉搜索树呢? 该节点应该小于其右子树的所有节点,大于其左子树的结点。 ------ 这就是我们的线索。所以我们应该找到其左子树上的最大的结点,或者是其右子树上的最小的结点。
Code:
TreeNode* Delete(ElementType x, TreeNode* T)
{
if(T == NULL) return NULL;
if(x > T -> Element){
T -> right = Delete(x, T -> right);
}
else if(x < T -> Element){
T -> left = Delete(x, T -> left);
}
// 找到需要删除的结点= =
if(x -> left && x -> right){ // 有两棵子树
// 用其右子树上最小值的结点代替该结点
TreeNode* submin = FindMin(T -> right);
T -> Element = submin -> Element;
T -> right = Delete(T -> Element, T -> right);
// 用其左子树上最大值的结点代替该结点
TreeNode* summax = FindMax(T -> left);
T -> Element = summax -> Element;
T -> left = Delete(T -> Element, T -> left);
}
else{ // 有一棵子树或者没有子树
TreeNode* tmp = T;
if(T -> left == NULL){
T = T -> right;
}
else if(T -> right == NULL){
T = T -> left;
}
free(tmp);
}
}
到此为止,我们的二叉搜索树的基本操作就是这些,只是一些很基础的一些东西,希望以后有所补充。