二叉搜索树的基本操作

来源:互联网 发布:有毒网络剧歌曲 编辑:程序博客网 时间:2024/05/19 16:50

二叉搜索树

 

什么是二叉搜索树?

  首先是一棵二叉树(5种形态),其次,二叉搜索树中的关键字满足以二叉搜索树的方式进行储存:设x是二叉搜索树中的 一个结点,如果yx左子树中的一个结点,那么y .key <= x.key;如果yx右子树中的一个结点,那么y.key >= x.key

除去找第K大数外,其他复杂度都是0h)级别。

 

一、遍历二叉搜索树:

  一般由于二叉搜索树是特殊的二叉树,所以遍历都是一样的。特别的对于二叉搜索树中序遍历  LVR结果就是对元素从小到大的顺序。 RVL是从大到小的顺序。

[证明

如果树为空,放入一个值x,按照中序输出后 有序。

如果不为空,并且有n个元素,如果不考虑有序性可以放入的位置为n+1个,但是 由于二叉搜索树的性质,所以只能在一个位置上插入,如果这个位置是某个节点y的右儿子(假

其没有左右儿子),那么y就是大于这个元素中最小的点,反之同理。       

<span style="font-size:18px;">void SortPriont(SeachTree *ST)//按从大到小的顺序输出{if(ST == NULL)  return ; SortPriont(ST->Right); printf("%d ",ST->Data); SortPriont(ST->Left);} void SortPriont(SeachTree *ST)//按从小到大的顺序输出{if(ST == NULL)  return ; SortPriont(ST->Left); printf("%d ",ST->Data); SortPriont(ST->Right);} </span>

二、遍历二叉搜索树中的一条路径:

  从某以结点开始,每次遍历只遍历其左孩子和右孩子其中一个这样就形成一条单一路径。

对于二叉搜索树中任意一条路径,只要存在某个父节点访问其左孩子,那么这条路径中这个父节点之后所有结点的关键字都比父节点关键字要小,当访问右孩子时,相反。但是在这条路径之外的结点与路径上结点关键字没有任何关系,这是由于二叉搜索树的性质所决定。

如果从任意一点到叶结点,那么整条路经以NULL结束。


1)查找某个关键字是否存在:

<span style="font-size:18px;">//传递树根和要找的关键字, 如果找到返回这个值地址,否则返回NULL。SeachTree *Find(ElementType item,SeachTree *ST){//递归 /*if(ST==NULL||ST->Data == item)  return ST;//返回NULL在这条最合法的路径中没有if(ST->Data < item)  return Find(item,ST->Right);else                 return Find(item,ST->Left);*///非递归 if(ST==NULL||ST->Data == item) return ST;else{SeachTree *temp = ST;while(temp!=NULL)    {    if(temp->Data == item )   return temp;    else    if(temp->Data < item )    temp = temp->Right;    else                      temp = temp->Left ;}return temp;//没有找到,此时temp为null。}}</span>


 

(2)查找最大(小)关键字:

以为只要存在某个父节点访问其左孩子,那么这条路径中这个父节点之后所有结点的关键字都比父节点关键字要小,所以如果在路径中的每个结点都访问其左孩子,那么最后一个就是最小的并且这条路径按照从大到小的顺序产生。

<span style="font-size:18px;">SeachTree *FindMin(SeachTree *ST){//递归 /*if(ST->Left==NULL)  return ST;return FindMin(ST->Left);*///非递归 SeachTree *temp=ST;while(temp->Left!=NULL){temp=temp->Left;}return temp;}</span>

查找最小的同理。

(3)前驱与后继:(适用于所有的二叉树)

  这里的前驱后继是相对于中序遍历来说的,所以一个结点x的后继就是大于x.key中最小的关键字的结点,前驱是小于x.key中最大的关键字的结点以求后继为例:

  除去边界点外,任意一个结点都有两个身份,一个是作为某个元素的后继,一个是作为某个元素的前驱。如果x结点有右儿子,则后继就是右子树中最小关键字的结点。同时这个结点的前驱就是x。如果x结点没有右儿子,那么x的后继一定是其祖先中的某个元素,这个元素的左子树最大值就是x,也就是说,x的后继就是这个祖先,而这个祖先的前驱就是x

所以这个祖先一定出现在最底层的,但是其左子树也是x祖先的祖先(x本身也是自己的祖先)。要特别注意对边界点的处理,即左边界点的前驱为NULL,右边节点的后继为NULL

   求前驱与后继也是找某条路径的过程,只不过这条路径的断点出元素比较特殊,对路径做出了限制。

<span style="font-size:18px;">SeachTree *SuccessOR(SeachTree *ST,ElementType item)//某个节点的后继{SeachTree *key = Find(item,ST);if(key == NULL) return NULL;if(key->Right != NULL){return FindMin(key->Right);    }    else    {    SeachTree *keyparent = key->parent;     if(keyparent != NULL && keyparent->Right == key)    {     key = keyparent;     keyparent = keyparent->parent;}return keyparent;}} SeachTree *PredecessOR(SeachTree *ST,ElementType item)//某个节点的前驱 {SeachTree *key = Find(item,ST);if(key == NULL) return NULL;if(key->Left != NULL){return FindMax(key->Left);}else{SeachTree *keyparent = key->parent;if(keyparent!=NULL && keyparent->Left == key){key = keyparent;keyparent = keyparent->parent;}return keyparent;}}</span>


 

(4)查找第K大数:

  就是用FindMin(SeachTree *ST)函数找到最小值,然后依次用SuccessOR(SeachTree *ST,ElementType item)找后继的过程,一共找K-1次后继。时间复杂度为0K+h)。

<span style="font-size:18px;">SeachTree * FindKNumber(SeachTree *ST, int k)//  k要保证有效{k--;SeachTree *N = FindMin(ST);while(k--){N= SuccessOR(ST,N->Data); }return N;}</span>

三、二叉搜索树的插入:

找某个关键字应该放到那条路径后面。


<span style="font-size:18px;">SeachTree *Insert(SeachTree *ST, ElementType item){SeachTree *pNew = NULL;pNew = (SeachTree *)malloc(sizeof(SeachTree));pNew->Data = item;pNew->Left = pNew->parent = pNew->Right = NULL;if(ST==NULL)//空树{ST = pNew;}else{SeachTree **temp = &ST,*parent=NULL;while(*temp!=NULL){parent = *temp;if((*temp)->Data <= item)   temp = &(*temp)->Right;else                        temp = &(*temp)->Left;}pNew->parent = parent;*temp = pNew;}return ST;}</span>


四、二叉搜索树的删除操作:

  

二叉搜索树删除最根本来说就是删除节点后让其前驱或者后继节点来代替递归下去,直至不需要“删除”操作为止。在中序遍历中体现出来的是将删除后之前(之后)元素向前(向后)移动一个单位。

  虽然对于删除一个节点的操作可以对应于好几种不同的选择,但是对于不同树的形态都有最优的拌饭或方便操作的办法。(一下假设删除的节点为根节点)

一、空树 判断后不做任何处理直接返回

二、只有一个根节点的树:删除之后直接为NULL 可以归纳到34情况中

三、只有一个根节点和一个左孩子:其理论上可以将后继(最低祖先其左孩子为祖先)放到删除位置或者直接讲前驱(左子树最大值)放到删除节点或者直接将左子树上移到删除列点处,然后调整。以上三种方法中最为简单的为最后一个。

四、只有一个根节点和右儿子:同上

五、有根节点和左右两个儿子:由四中方式可以实现,此处省略,但是最简单的为找到右子树最小的元素,然后将其替代被删除的元素,然后调整。

(如果找到的最小的元素为右子树的根节点则直接上移替代,否则要多加一部对最小值删除的操作)


<span style="font-size:18px;">void Transplant(SeachTree **ST,SeachTree *u,SeachTree *v)//  u  = v  v可能为NULL u可能为根节点 {if(u->parent == NULL){*ST = v; }else{if(u->parent->Left == u){u->parent->Left = v;}elseif(u->parent->Right == u){u->parent->Right = v;}}if(v != NULL)  v->parent = u->parent;}ElementType Delete(SeachTree **ST,ElementType item){SeachTree *z =Find(item,*ST); if(z==NULL) return NULL;if(z->Left == NULL)// 只有右孩子或者没有孩子时 {Transplant(ST,z,z->Right);}elseif(z->Right == NULL)//只有左孩子    {Transplant(ST,z,z->Left);}else//左右孩子都存在           {SeachTree * y= FindMin(z->Right);//找后继    if(y->parent != z){Transplant(ST,y,y->Right);y->Right = z->Right;y->Right->parent = y;}Transplant(ST,z,y);y->Left = z->Left;y->Left->parent = y;}free(z);}</span>

完整代码:

<span style="font-size:18px;">/**************************************************************  project :二叉搜索树的基本操作  author  :wmaoshu   Email   :wmaoshu@163.com   time    :2015/11/01***************************************************************/ #include<stdio.h>#include<string.h>#include<stdlib.h>typedef int ElementType;struct TreeNode{ElementType Data;struct TreeNode *Left;struct TreeNode *Right;struct TreeNode *parent;};typedef struct TreeNode SeachTree;//=================================================SeachTree *Find(ElementType item,SeachTree *ST);SeachTree *FindMax(SeachTree *ST);SeachTree *FindMin(SeachTree *ST);SeachTree *SuccessOR(SeachTree *ST,ElementType item);SeachTree *PredecessOR(SeachTree *ST,ElementType item); SeachTree *Insert(SeachTree *ST, ElementType item); ElementType Delete(SeachTree **ST,ElementType item);void SortPriont(SeachTree *ST); // 查找三个基本操作 /***********************************************************任何一棵树的最左边值为这棵树中所有结点的max值任何一棵树的最右边值为这棵树中所有结点的min值(时间复杂度都和高度成正比 ) ************************************************************/ SeachTree *Find(ElementType item,SeachTree *ST){//递归 /*if(ST==NULL||ST->Data == item)  return ST;if(ST->Data < item)  return Find(item,ST->Right);else                 return Find(item,ST->Left);*///非递归 if(ST==NULL||ST->Data == item) return ST;else{SeachTree *temp = ST;while(temp!=NULL)    {    if(temp->Data == item )   return temp;    else    if(temp->Data < item )    temp = temp->Right;    else                      temp = temp->Left ;}return temp;}}SeachTree *FindMax(SeachTree *ST){//递归 /*if(ST->Right==NULL) return ST;return FindMax(ST->Right);*///非递归 SeachTree * temp = ST;while(temp->Right!=NULL){temp=temp->Right;}return temp;}SeachTree *FindMin(SeachTree *ST){//递归 /*if(ST->Left==NULL)  return ST;return FindMin(ST->Left);*///非递归 SeachTree *temp=ST;while(temp->Left!=NULL){temp=temp->Left;}return temp;}//查找某元素的前驱和后继SeachTree *SuccessOR(SeachTree *ST,ElementType item)//某个节点的后继{SeachTree *key = Find(item,ST);if(key == NULL) return NULL;if(key->Right != NULL){return FindMin(key->Right);    }    else    {    SeachTree *keyparent = key->parent;    //不能用左值为NULL作为判断条件,因为不知道keyparent的左值是否为NULL     if(keyparent != NULL && keyparent->Right == key)    {    key = keyparent;    keyparent = keyparent->parent;}return keyparent;}} SeachTree *PredecessOR(SeachTree *ST,ElementType item)//某个节点的前驱 {SeachTree *key = Find(item,ST);if(key == NULL) return NULL;if(key->Left != NULL){return FindMax(key->Left);}else{SeachTree *keyparent = key->parent;if(keyparent!=NULL && keyparent->Left == key){key = keyparent;keyparent = keyparent->parent;}return keyparent;}}//插入 SeachTree *Insert(SeachTree *ST, ElementType item){SeachTree *pNew = NULL;pNew = (SeachTree *)malloc(sizeof(SeachTree));pNew->Data = item;pNew->Left = pNew->parent = pNew->Right = NULL;if(ST==NULL){ST = pNew;}else{SeachTree **temp = &ST,*parent=NULL;while(*temp!=NULL){parent = *temp;if((*temp)->Data <= item)   temp = &(*temp)->Right;else                        temp = &(*temp)->Left;}pNew->parent = parent;*temp = pNew;}return ST;}//删除 void Transplant(SeachTree **ST,SeachTree * key, SeachTree *newkey){if(key->parent==NULL) *ST=newkey;else if(key->parent->Left==key) key->parent->Left = newkey;else                       key->parent->Right=newkey;if(newkey!=NULL) newkey->parent = key->parent;}ElementType Delete(SeachTree **ST,ElementType item){SeachTree *key = Find(item,*ST);if(key->Left == NULL){Transplant(ST,key,key->Right); }  else if(key->Right == NULL) { Transplant(ST,key,key->Left); } else { SeachTree * newkey = FindMin(key->Right); if(newkey->parent!=key) { Transplant(ST,newkey,newkey->Right); newkey->Right = key->Right; newkey->Right->parent = newkey;}Transplant(ST,key,newkey);newkey->Left = key->Left;newkey->Left->parent = newkey;  }}//输出/*************************************************************中序遍历的输出结果正式对树中元素的从小到大排序证明:如果树为空,放入一个值x,按照中序输出后 有序。如果不为空,并且有n个元素,如果不考虑有序性可以查的位置为n+1个但是 由于二叉搜索树的性质,所以只能在一个位置上插入,如果这个位置是某个节点y的右儿子,那么y就是大于x中最小的点,反之同理。 *************************************************************/void SortPriont(SeachTree *ST)//按从小到大的顺序输出{if(ST == NULL)  return ; SortPriont(ST->Left); printf("%d ",ST->Data); SortPriont(ST->Right);}  //==================================================int main(void){return 0;} </span>



0 0
原创粉丝点击