一直对于二叉搜索树(又叫二叉排序树,也叫二叉查找树),没有很好的理解,决定花点时间来学习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.csdn.net/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); } }
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/u011394362/article/details/46822571