zoukankan      html  css  js  c++  java
  • 算法导论 第12章 二叉查找树

    一、概念

    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 #include <iostream>
      2 #include <string>
      3 using namespace std;
      4 
      5 struct BST_Node
      6 {
      7 public:
      8     int key;//关键字
      9     int data;//卫星数据
     10     BST_Node *left;//左孩子
     11     BST_Node *right;//右孩子
     12     BST_Node *p;//父结点
     13 
     14     BST_Node(int x):key(x),data(x),left(NULL),right(NULL),p(NULL){}
     15 };
     16 
     17 //二叉查找树的结构
     18 class BST_Tree
     19 {
     20 public:
     21     BST_Node *root;
     22     BST_Tree():root(NULL){}    
     23 public:
     24     //12.1 二叉查找树
     25     void Inorder_Tree_Walk(BST_Node *x);
     26     //12.2 查询二叉查找树
     27     BST_Node *Tree_Search(BST_Node *x, int k);
     28     BST_Node *Iterative_Tree_Search(BST_Node *x, int k);
     29     BST_Node *Iterative_Tree_Minimum(BST_Node *x);
     30     BST_Node *Iterative_Tree_Maximum(BST_Node *x);
     31     BST_Node *Tree_Successor(BST_Node *x);
     32     //12.3 插入和删除
     33     void Iterative_Tree_Insert(BST_Node *z);
     34     BST_Node *Tree_Delete(BST_Node *z);
     35 };
     36 /*************12.1 二叉查找树****************************************************************/
     37 //递归的中序遍历
     38 void BST_Tree::Inorder_Tree_Walk(BST_Node *x)
     39 {
     40     if(x != NULL)
     41     {
     42         //中序遍历当前结点的左子树
     43         Inorder_Tree_Walk(x->left);
     44         //访问当前结点
     45         cout<<x->key<<endl;
     46         //中序遍历当前结点的右子树
     47         Inorder_Tree_Walk(x->right);
     48     }
     49 }
     50 /********12.2 查询二叉查找树****************************************************************/
     51 //递归地查询二叉查找树
     52 BST_Node *BST_Tree::Tree_Search(BST_Node *x, int k)
     53 {
     54     //找到叶子结点了还没找到,或当前结点是所查找的结点
     55     if(x == NULL || k == x->key)
     56         return x;
     57     //所查找的结点位于当前结点的左子树
     58     if(k < x->key)
     59         return Tree_Search(x->left, k);
     60     //所查找的结点位于当前结点的左子树
     61     else
     62         return Tree_Search(x->right, k);
     63 }
     64 //非递归的查询二叉查找树
     65 BST_Node *BST_Tree::Iterative_Tree_Search(BST_Node *x, int k)
     66 {
     67     //不是叶子结点且不是所查找的结点
     68     while(x != NULL && k != x->key)
     69     {
     70         //所查找的结点位于当前结点的左子树
     71         if(k < x->key)
     72             x = x->left;
     73         //所查找的结点位于当前结点的右子树
     74         else
     75             x = x->right;
     76     }
     77     return x;
     78 }
     79 //非递归找最小值
     80 BST_Node *BST_Tree::Iterative_Tree_Minimum(BST_Node *x)
     81 {
     82     //只要有比当前结点小的结点
     83     while(x->left != NULL)
     84         x = x->left;
     85     return x;
     86 }
     87 //非递归找最大值
     88 BST_Node *Iterative_Tree_Maximum(BST_Node *x)
     89 {
     90     //只要有比当前结点小的结点
     91     while(x->right != NULL)
     92         x = x->right;
     93     return x;
     94 }
     95 //查找中序遍历下x结点的后继,后继是大于key[x]的最小的结点
     96 BST_Node *BST_Tree::Tree_Successor(BST_Node *x)
     97 {
     98     //如果有右孩子
     99     if(x->right != NULL)
    100         //右子树中的最小值
    101         return Iterative_Tree_Minimum(x->right);
    102     //如果x的右子树为空且x有后继y,那么y是x的最低祖先结点,且y的左儿子也是
    103     BST_Node *y = x->p;
    104     while(y != NULL && x == y->right)
    105     {
    106         x = y;
    107         y = y->p;
    108     }
    109     return y;
    110 }
    111 /*********12.3插入和删除**********************************************************/
    112 //二叉查找树的插入,非递归
    113 void BST_Tree::Iterative_Tree_Insert(BST_Node *z)
    114 {
    115     //找到要插入的位置
    116     BST_Node *x = root, *y = NULL;
    117     //若x为空,x是要插入的位置,x的父是z->p
    118     while(x != NULL)
    119     {
    120         y = x;
    121         if(z->key == x->key)
    122         {
    123             cout<<"error:exist"<<endl;
    124             return;
    125         }
    126         if(z->key < x->key)
    127             x = x->left;
    128         else
    129             x = x->right;
    130     }
    131     //修改指针,注意树为空的情况
    132     z->p = y;
    133     if(y == NULL)
    134         root = z;
    135     else if(z->key < y->key)
    136         y->left = z;
    137     else y->right = z;
    138 }
    139 //二叉查找树的删除,实际删除的不一定是z,可能是z的后继
    140 //然后把z的值改为实际删除结点的值
    141 BST_Node *BST_Tree::Tree_Delete(BST_Node *z)
    142 {
    143     BST_Node *x, *y;
    144     //若z最多只有一个孩子,实际删除的结点是z
    145     if(z->left == NULL || z->right == NULL)
    146         y = z;
    147     //若z有两个孩子,实际删除的结点是z的后继
    148     else
    149         y = Tree_Successor(z);
    150     //用x表示"实际要删除的结点"的孩子(最多一个孩子)
    151     if(y->left != NULL)
    152         x = y->left;
    153     else
    154         x = y->right;
    155     //修改指针,以删去结点
    156     if(x != NULL)//若"实际要删除的结点"没有孩子
    157         x->p = y->p;
    158     if(y->p == NULL)//若"实际要删除的结点"是根结点
    159         root = x;
    160     else if(y == y->p->left)
    161         y->p->left = x;
    162     else y->p->right = x;
    163     //"若初阶要删除的结点"不是"待删除的结点",则内容替代
    164     if(y != z)
    165     {
    166         z->key = y->key;
    167         z->data = y->data;
    168     }
    169     return y;
    170 }

    三、练习

    12.1 二叉查找树

    12.1-2

    二叉查找树:左子树关键字<根结点关键字<右子树关键字

    堆:左子树关键字<根结点关键字 && 右子树关键字<根结点关键字

    不能,因为一个结点的的左子树与右子树的关键字大小没有关系

    12.1-3

    用栈实现:见算法导论-10.4-有根树的表示中的10.4-3

    不用栈实现:见算法导论-10.4-5

    12.1-4

     1 //递归的先序遍历  
     2 void BST_Tree::Preorder_Tree_Walk(BST_Node *x)  
     3 {  
     4     //x不是叶子结点  
     5     if(x != NULL)  
     6     {  
     7         //访问当前结点  
     8         cout<<x->key<<' ';  
     9         //先序遍历当前结点的左子树  
    10         Preorder_Tree_Walk(x->left);  
    11         //先序遍历当前结点的右子树  
    12         Preorder_Tree_Walk(x->right);  
    13     }  
    14 }
    15 //递归的后序遍历  
    16 void BST_Tree::Postorder_Tree_Walk(BST_Node *x)  
    17 {  
    18     //x不是叶子结点  
    19     if(x != NULL)  
    20     {  
    21         //后序遍历当前结点的左子树  
    22         Postorder_Tree_Walk(x->left);  
    23         //后序遍历当前结点的右子树  
    24         Postorder_Tree_Walk(x->right);  
    25         //访问当前结点  
    26         cout<<x->data<<' ';  
    27     }  
    28 }

    12.2 查询二叉查找树

     1 12.2-1
     2 c,e
     3 12.2-2
     4 //递归地查找最小值  
     5 BST_Node *BST_Tree::Tree_Minimum(BST_Node *x)  
     6 {  
     7     if(x->left != NULL)  
     8         return Tree_Minimum(x->left);  
     9     else return x;  
    10 }  
    11 //递归的查找最大值  
    12 BST_Node *BST_Tree::Tree_Maximum(BST_Node *x)  
    13 {  
    14     if(x->right != NULL)  
    15         return Tree_Maximum(x->right);  
    16     else return x;  
    17 } 
    18 12.2-3
    19 //查找中序遍历下x的前驱,即小于x的最大值  
    20 BST_Node *BST_Tree::Tree_Predecessor(BST_Node *x)  
    21 {  
    22     //如果x的左子树非空  
    23     if(x->left != NULL)  
    24         //x的前驱是x的左子树的最大值  
    25         return Tree_Maximum(x->left);  
    26     //如果x的左子树为空且x有前驱y,那么y是x的最低祖先结点,且y的右儿子也是  
    27     BST_Node *y = x->p;  
    28     while(y != NULL && x == y->left)  
    29     {  
    30         x = y;  
    31         y = y->p;  
    32     }  
    33     return y;  
    34 }  
    35 12.2-4
    36137 4->left = 2 4->right =NIL
    38 2->left = 1 2->right = 3
    39 搜索路径4-2-1
    40241 1->right = 3 1->left = NUL
    42 3->left = 2 3->right = 4
    43 搜索路径1-3-4

    12.3 插入和删除

    12.3-1

     1 //递归的二叉查找树的插入操作,分三种情况  
     2 void BST_Tree::Tree_Insert(BST_Node *x, BST_Node *z)  
     3 {  
     4     //已经存在  
     5     if(z->key == x->key)  
     6     {  
     7         cout<<"error:exist"<<endl;  
     8         return;  
     9     }  
    10     //插入到x的左子树中  
    11     else if(z->key < x->key)  
    12     {  
    13         //x没有左子树  
    14         if(x->left == NULL)  
    15         {  
    16             //修改指针,插入操作  
    17             x->left = z;  
    18             z->p = x;  
    19             return;  
    20         }  
    21         //x有左子树  
    22         else  
    23             //对x的左子树执行插入操作  
    24             Tree_Insert(x->left, z);  
    25     }  
    26     //插入到x的右子树中,与上面类似  
    27     else if(z->key > x->key)  
    28     {  
    29         if(x->right == NULL)  
    30         {  
    31             x->right = z;  
    32             z->p = x;  
    33         }  
    34         else  
    35             Tree_Insert(x->right, z);  
    36     }  
    37 } 

    12.3-3

    最坏是n^2

    最好是nlgn

    12.3-4

    求y的前驱z分为两种情况,以下分别讨论:

    (1)y有左孩子,则z是left[y]中最右边的结点,z没有右孩子,因此删除z时直接删除修改指针即可,没有问题

    (2)y没有左孩子,则z是y的祖先,y是z右子树是最左边的点,又分为两种情况:

    (2.1)若z没有左孩子,则直接删除z并修改指针,没有问题。

    (2.2)若z有左孩子,则不直接删除z,而是用z代替y存在并删除y。这里会有问题,另一个数据结构中的保存了指向y的指针,但是y的内容转移到另一个结点上了,指向y的指针指向了一个被释放的空间。

    解决方法:使TREE-DELETE返回删除后的y的指针,这个值可能会变,可能不变。让另一个数据结构的y指针指向TREE-DELETE的返回值。

    12.3-5

    不或交换,反例如图

     

    12.3-6

    当待删除结点有两个子树时,不删除待删除结点,而是删除它的前驱或后继,用随机数rand()%2来确定删除的前驱还是后继

    代码见文:二

    四、思考题

    12-1 具有相同关键字元素的二叉树

    算法导论-12-1-具有相同关键字元素的二叉查找树

    12-2 基数树

    算法导论-12-2-基数树

    12-3 随机构造的二叉查找树中的平均结点深度

    f)待解决

  • 相关阅读:
    简单查询
    Scott用户表
    记一次Sqoop抽数据异常
    Flink+Druid构建实时OLAP的探索
    客户端埋点实时OLAP指标计算方案
    Kafka服务不可用(宕机)问题踩坑记
    实时计算-多级订单金额,及下级人数
    Apache Druid0.15.0安装方式
    superset安装文档
    Scala的常用小技巧
  • 原文地址:https://www.cnblogs.com/windmissing/p/3780675.html
Copyright © 2011-2022 走看看