一直对于二叉搜索树(又叫二叉排序树,也叫二叉查找树),没有非常好的理解,决定花点时间来学习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); } }
到此为止,我们的二叉搜索树的基本操作就是这些,仅仅是一些非常基础的一些东西,希望以后有所补充。