[算法导论]第十二章《二叉搜索树》

来源:互联网 发布:最优化方法例题 编辑:程序博客网 时间:2024/05/14 03:39

一、概念

1.定义与性质

(1)设x为二叉查找树中的一个结点,若y是x左子树中的一个结点,则key[y] <= key[x];若y是x右子树中的一个结点,则key[x]<=key[y]
(2)二叉查找树上执行的基本操作的时间与树的高度成正比。

2.结构

(1)结点结构:

关键字key
卫星数据data
分别指向父、左右孩子的指针p, left, right

3.在二叉查找树上的操作

查找一个关键字:SEARCH(x, k)
求最小关键字:MINIMUM(x)
求最大关键字:MAXIMUM(x)
求前驱:PREDECESSOR(x)
求后继:SUCCESSOR(x)
插入一个结点:INSERT(T, z)
删除一个结点:DELETE(z)

4.二叉查找树的应用

1.遍历:中序遍历、先序遍历、后序遍历
2.查找:查找包含某个关键字的结点,查找关键字最大或最小的结点、查找某个结点的前驱或后继

一棵二叉树查找及其中根遍历结果如下图所示:

(1)查找

  在二叉查找树中查找一个给定的关键字k的过程与二分查找很类似,根据二叉查找树在的关键字存放的特征,很容易得出查找过程:首先是关键字k与树根的关键字进行比较,如果k大比根的关键字大,则在根的右子树中查找,否则在根的左子树中查找,重复此过程,直到找到与遇到空结点为止。例如下图所示的查找关键字13的过程:(查找过程每次在左右子树中做出选择,减少一半的工作量)

书上查找过程的递归和非递归形式的伪代码:

TREE_SEARCH(x,k)   if x=NULL or k=key[x]       then return x   if(k<x.key)       then return TREE_SEARCH(x.left,k)    else       then return TREE_SEARCH(x.right,k)


ITERATIVE_TREE_SEARCH(x,k)  while x!=NULL and k!=x.key      do if k<x.key              then x=x.left           else              then x=x.right   return x

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

  根据二叉查找树的特征,很容易查找出最大和最小关键字。查找二叉树中的最小关键字:从根结点开始,沿着各个节点的left指针查找下去,直到遇到NULL时结束。如果一个结点x无左子树,则以x为根的子树中,最小关键字就是key[x]。查找二叉树中的最大关键字:从根结点开始,沿着各个结点的right指针查找下去,直到遇到NULL时结束。书中给出了查找最大最小关键字的伪代码:

TREE_MINMUM(x)   while x.left != NULL       do x=x.left    return x


 TREE_MAXMUM(x)    while x.right != NULL         do x= x.right     return x

(3)前驱和后继

  查找前驱步骤:先判断x是否有左子树,如果有则在left[x]中查找关键字最大的结点,即是x的前驱。如果没有左子树,则从x继续向上执行此操作,直到遇到某个结点是其父节点的右孩子结点。例如下图查找结点7的前驱结点6过程:

  查找后继步骤:先判断x是否有右子树,如果有则在right[x]中查找关键字最小的结点,即使x的后继。如果没有右子树,则从x的父节点开始向上查找,直到遇到某个结点是其父结点的左儿子的结点时为止。例如下图查找结点13的后继结点15的过程:

书中给出了求x结点后继结点的伪代码:

 TREE_PROCESSOR(x)     if x.right != NULL         then return TREE_MINMUM(x.right)     y=x.p     while y!= NULL and x ==y.right            do x = y                y=y.p     return y

定理:对一棵高度为h的二叉查找,动态集合操作SEARCH、MINMUM、MAXMUM、SUCCESSOR、PROCESSOR等的运行时间均为O(h)。

3、插入和删除

  插入和删除会引起二叉查找表示的动态集合的变化,难点在在插入和删除的过程中要保持二叉查找树的性质。插入过程相当来说要简单一些,删除结点比较复杂。

(1)插入

  插入结点的位置对应着查找过程中查找不成功时候的结点位置,因此需要从根结点开始查找带插入结点位置,找到位置后插入即可。下图所示插入结点过程:

书中给出了插入过程的伪代码:

TREE_INSERT(T,z)    y = NULL;    x =T.root    while x != NULL        do y =x            if z.key < z.key                 then x=x.left                 else  x=x.right     z.p =y     if y=NULL        then T.root =z        else if z.key>y.key                   then  y.left  = z                   else   y.right =z

插入过程运行时间为O(h),h为树的高度。

(2)删除

  从二叉查找树中删除给定的结点z,分三种情况讨论:

<1>结点z没有左右子树,则修改其父节点p[z],使其为NULL。删除过程如下图所示:

<2>如果结点z只有一个子树(左子树或者右子树),通过在其子结点与父节点建立一条链来删除z。删除过程如下图所示:

<3>如果z有两个子女,则先删除z的后继y(y没有左孩子),在用y的内容来替代z的内容。

书中给出了删除过程的伪代码:

TREE_DELETE(T,z)    if z.left ==NULL or z.right == NULL       then y=z       else  y=TREE_SUCCESSOR(z)   if y.left != NULL       then x=y.left       else  x=y.right   if x!= NULL       then x.p = y.p   if y.p ==NULL      then Troot =x      else if y = y.p.left                  then y.p.left = x                  else  y.p.right =x    if y!=z        then z.key = y.key              copy y's data into z     return y

定理:对高度为h的二叉查找树,动态集合操作INSERT和DELETE的运行时间为O(h)。

代码的C++实现:

/* *作者:RogerKing *邮箱: jin_tengfei@163.com *日期:2014-03-19-10.15 */#include <iostream>using namespace std;typedef struct TreeNode{int key;TreeNode*left;//左孩子TreeNode*right;//右孩子TreeNode*pre;//指向父节点的指针}*TreePoint;/*************12.1二叉搜索树*****************************///递归的中序遍历void Inorder_Tree_Walk(TreePoint x){if (x!=NULL){//中序遍历当前结点的左子树Inorder_Tree_Walk(x->left);//访问当前结点cout<<x->key<<" ";//中序遍历当前结点的右子树Inorder_Tree_Walk(x->right);}}/*************12.2查询二叉搜索树*************************///递归查询二叉搜索树TreePoint Tree_Search(TreePoint x,int k){//找到叶子结点了还没找到,或当前结点是所查找的结点if(x==NULL||x->key==k)return x;//所查找的结点位于当前结点的左子树if (k<x->key)return Tree_Search(x=x->left,k);//所查找的结点位于当前结点的左子树elsereturn Tree_Search(x=x->right,k);}//非递归查询二叉搜索树TreePoint Iterative_Tree_Search(TreePoint x,int k){//不是叶子结点且不是所查找的结点    while (x!=NULL&&x->key!=k)    {//所查找的结点位于当前结点的左子树        if (k<x->key)x=x->left;        //所查找的结点位于当前结点的右子树else            x=x->right;    }    return x;}//查找树中最小元素TreePoint Tree_Minimum(TreePoint x){while (x->left){x=x->left;}return x;}//查找树中最大元素TreePoint Tree_Maximum(TreePoint x){while (x->right){x=x->right;}return x;}//查找某个节点中序遍历情况下的后继节点,后继是大于key[x]的最小的结点TreePoint Tree_SUCCESSOR(TreePoint x){if(x == NULL)    {        return NULL;    }//如果右子树不为空,则后继为其右子树的最小值节点else if (x->right!=NULL){return Tree_Minimum(x->right);}TreePoint y=x->pre;while (y!=NULL&&x==y->right){x=y;y=y->pre;}return y;}//查找某个节点中序遍历情况下的前驱节点,前驱是小于key[x]的最大的结点 。TreePoint Tree_Predecessor(TreePoint x){    if(x == NULL)    {        return NULL;    }//如果左子树不为空,则前驱为其左子树的最大值节点    else if(x->left != NULL)    {        return Tree_Maximum(x->left);    }    TreePoint t=x->pre;    while((t != NULL) && (x == t->left))    {x = t;t = t->pre;}return t;}/*************12.3插入和删除*****************************///采用循环而非递归的插入树节点。void Tree_Insert(TreePoint& T,int key){TreePoint x=T,y=NULL,z = new TreeNode;z->key=key;z->left=z->right=z->pre=NULL;//树中有节点时找到插入节点需要插入的位置while (x!=NULL){y=x;if (key<x->key){x=x->left;}elsex=x->right;}z->pre=y;if (y==NULL)//树为空时{T=z;}else{if (key<y->key){y->left=z;}else{y->right=z;}}}//二叉搜索树的删除void Tree_Delete(TreePoint root,TreePoint z){TreePoint x=NULL,y;//若z最多只有一个孩子,实际删除的结点是zif (z->left==NULL||z->right==NULL)y=z;//若z有两个孩子,实际删除的结点是z的后继elsey=Tree_SUCCESSOR(z);//用x表示"实际要删除的结点"的孩子(最多一个孩子)if (y->left!=NULL)x=y->left;elsex=y->right;//修改指针,以删去结点if (x!=NULL)//若"实际要删除的结点"没有孩子x->pre=y->pre;if (y->pre==NULL)//若"实际要删除的结点"是根结点root=x;else if (y==y->pre->left)     y->pre->left=x;    else     y->pre->right=x;//"若初阶要删除的结点"不是"待删除的结点",则内容替代if (y!=z)z->key=y->key;}int main(){TreePoint root=NULL;int a[14]={15,5,26,35,2,20,17,3,18,24,6,1,10,7};for (int i=0;i<14;i++){Tree_Insert(root,a[i]);}cout<<"中序遍历树结果\n";Inorder_Tree_Walk(root);cout<<endl;cout<<"插入元素:19 ";Tree_Insert(root,19);cout<<endl;cout<<"插入元素后中序遍历树结果\n";Inorder_Tree_Walk(root);cout<<endl;cout<<"查找元素15:";TreePoint p=Tree_Search(root,15);if (p){cout<<"已经找到元素 \n";}elsecout<<"没有找到元素 \n";cout<<"元素15的前驱节点:";cout<<Tree_Predecessor(p)->key<<" "<<(Tree_Predecessor(p) != NULL ? "查找成功" : "查找失败")<<endl;p=Tree_Search(root,20);cout<<"元素20的后继节点:";cout<<Tree_SUCCESSOR(p)->key<<" "<<(Tree_SUCCESSOR(p) != NULL ? "查找成功" : "查找失败")<<endl;cout<<"删除树中值为10的元素\n";p=Tree_Search(root,10);Tree_Delete(root,p);Inorder_Tree_Walk(root);cout<<endl;return 0;}

结果显示:


参考文章:http://www.cnblogs.com/Anker/archive/2013/01/28/2880581.html










0 0
原创粉丝点击