zoukankan      html  css  js  c++  java
  • 算法打基础——二叉查找树Ⅰ

    二叉查找树是一种基本的数据结构。 它的优势在于其高效的查询,排序过程,且它也支持多种操作,如插

    入、删除、前趋,后接等。二叉查找树常被用于其他抽象结构的一个基础,比如字典、优先队列、集合、多

    集等。总之,就是用处多多。

    二叉查找树基本上各种操作的效率跟树的高度都是直接成比例的。所以查找树的结构就非常重要了,平衡的

    结构可使得效率更高。这就带来了随机化版本的二叉树。 具体各种操作的分析在这一节中讲,随机化版本的

    分析涉及数学很多,放在下一讲中,下一讲实际上也写好了,就放在候选区了~ 

    这一讲的主要知识点有:1.二叉树基本结构  2.查找操作  3.插入删除操作

     

    1.二叉树基本结构

    二叉树的每个节点(node)就是一个对象,每个node里主要有key、卫星数据(就是主要数据)、left(存储左儿

    子), right(存储右儿子),parent(存储父节点)。如果相应节点不存在,则为NULL

    二叉查找树的组织方式很简单,当一个节点进来后,与树中节点比较,如果大于当前节点key,就进入它的右

    子树,否则进入左子树。知道找到NULL的位置。下面是两个BST的例子

     二叉树的一个基本操作是树的遍历。树的遍历也主要有三种:前序、中序、后序。其主要区别看下面中序

    的伪代码就可以很好理解了。

    INORDER-TREE-WALK(x)

    1   if x != NIL
    2     INORDER-TREE-WALK(x.left)
    3     print x.key
    4     INORDER-TREE-WALK(x.right)

    中序就是打印的命令放在了左、右递归的中间。 而实际上,根据二叉查找树的性质,中序遍历打印出来的结

    构就是所有键值按从小到大顺序排序后的结果!

    树的遍历作为一种遍历,当然其时间复杂度就是Θ(n)

     

     2.查找操作

    二叉查找树作为一种查找树,当然常见操作就是查找了。 查询除了常用的查找某个关键字之外,还有找最

    大、最小、后续、前趋关键字。对高度为h的的树,这些操作的时间复杂度都是Θ(h)

     查找关键字

    TREE-SEARCH(x; k)
    1   if x == NIL or k == x.key
    2     return x
    3   if k < x.key
    4     return TREE-SEARCH(x.left; k)
    5   else return TREE-SEARCH(x.right; k)

    其过程是沿着树的根节点比较着往下找,大则右,小则左。所以时间复杂度是O(h)

    最大关键字与最小关键字

    TREE-MINIMUM(x)

    1   while x.left != NULL
    2     x = x.left
    3   return x

    TREE-MAXIMUM(x)
    1   while x.right != NULL
    2     x = x.right
    3   return x

    最大与最小是分别沿着右子树或左子树一直下降,时间复杂度O(h)

    前趋和后续 

    TREE-SUCCESSOR(x)
    1   if x.right != NULL
    2     return TREE-MINIMUM(x.right)
    3   y = x.p
    4   while y != NIL and x == y.right
    5     x = y
    6     y = y.p
    7   return y

    后继操作的过程需要分为两种情况:1 存在右子树,则是右子树当中的最小值(比它大的元素里面的最小值,合

    理!);2 只有左子树,则顺流而上,找到第一次做左子树的地方。图例见下:case1-15  case2-13

    前趋的过程与后继是对称的,伪代码就不写了,后面附的代码里面会有。

     

     3.插入删除操作

    插入与删除操作因为需要改变一些东西,且还要维持数据结构的性质,稍微会复杂一些

    TREE-INSERT(T; z)
    1   y = NULL
    2   x = T.root
    3   while x != NULL
    4     y = x
    5     if z.key < x.key
    6     x = x.left
    7     else x = x.right
    8   z.p = y
    9   if y == NULL
    10     T.root = z   // tree T was empty
    11  elseif z.key < y.key
    12     y.left = z
    13  else y.right = z

    在伪代码里面,y始终指向x的父节点。实现主要注意当是空树的时候,需要将当前节点作为根。否则就是一路下

    去直到找到空位置

    删除操作比较复杂,需要分为三种情况,所以这里先做分析。 三种情况对应不同的操作方法,1是既没有左子树

    也没有右子树  2只有一个子树 3同时有两个子树。对于1直接操作即可; 对于2删除当前节点时,将其子树接到

    节点的父节点上去;对于3,需要找到待删除节点的后继节点,然后用这个后继节点替代当前节点并删除那个后继

    节点之前的位置。 注意:找到的后继节点是没有左子树的否则它的前趋就不可能是那个待删除节点了。下面用

    图示例这三种情况:

    下面给出伪代码:

    TREE-DELETE(T, z)
    1   if left[z] = NIL or right[z] = NIL
    2     then y ← z
    3     else y ← TREE-SUCCESSOR(z)
    4   if left[y] = NIL
    5     then x ← left[y]
    6     else x ← right[y]
    7   if x = NIL
    8     then p[x] ← p[y]
    9   if p[y] = NIL
    10    then root[T ] ← x
    11    else if y = left[p[y]]
    12       then left[p[y]] ← x
    13       else right[p[y]] ← x
    14   if y = z
    15    then key[z]← key[y]
    16      copy y’s satellite data into z
    17   return y

    其过程与上面的三种情况顺序是不同的。 首先y是记录需要删除的节点的,即y必然有一个子树是空的;x是记录

    y的一个子节点的,若有就是相应子节点,没有则默认是右边的(Row 6); Row9-13是执行将删除点父节点的更

    新操作。Row14-16 是如果需要是替换的情况作一个替换。

     

     下面附上C++实现的BST:

      1 #include<iostream>
      2 #include<cstdlib>
      3 #include<ctime>
      4 using namespace std;
      5 
      6 #define random(x)(rand()%x)
      7 
      8 class BSTree;
      9 
     10 class Node{
     11 public:
     12     int key;
     13     Node* lchild;
     14     Node* rchild;
     15     Node* parent;
     16     Node(int value, Node* lc, Node* rc,Node* pa):key(value),
     17         lchild(lc),rchild(rc),parent(pa){};
     18     friend class BSTree;
     19 };
     20 
     21 class BSTree
     22 {
     23 public:
     24     Node* root;
     25     BSTree(Node* r){root=r;}
     26     void inorderwalk(Node* t) const;
     27     Node* search(Node* t,int ele)const;
     28     Node* findmax(Node* t) const;
     29     Node* findmin(Node* t) const;
     30     Node* predecessor(Node* t) const;
     31     Node* successor(Node* t) const;
     32     void insert(int k, Node* &t);
     33     void dele(int k, Node* &t);
     34 };
     35 
     36 void BSTree::inorderwalk(Node* t) const
     37 {
     38     if(t!=NULL)
     39     {
     40         inorderwalk(t->lchild);
     41         cout<<t->key<<" ";
     42         inorderwalk(t->rchild);
     43     }
     44 }
     45 Node* BSTree::search(Node* t, int ele) const
     46 {
     47     if(t==NULL)
     48     {
     49         cout<<"Not Found"<<endl;
     50         return t;
     51     }
     52     if(t->key==ele)
     53     {
     54         return t;
     55     }
     56     if(ele< (t->key))
     57         return search(t->lchild,ele);
     58     else return search(t->rchild,ele);
     59 }
     60 
     61 Node* BSTree::findmax(Node *t) const
     62 {
     63     while(t->rchild!=NULL)
     64     {
     65         t=t->rchild;
     66     }
     67     return t;
     68 }
     69 Node* BSTree::findmin(Node *t) const
     70 {
     71     while(t->lchild!=NULL)
     72     {
     73         t=t->lchild;
     74     }
     75     return t;
     76 }
     77 Node* BSTree::predecessor(Node *t) const
     78 {
     79     if(t->lchild!=NULL)
     80     {
     81         return findmax(t->lchild);
     82     }
     83     while(t->parent->lchild==t)
     84     {
     85         t=t->parent;
     86     }
     87     return t->parent;
     88 }
     89 Node* BSTree::successor(Node* t) const
     90 {
     91     if(t->rchild!=NULL)
     92     {
     93         return findmin(t->rchild);
     94     }
     95     while(t->parent->rchild==t)
     96     {
     97         t=t->parent;
     98     }
     99     return t->parent;
    100 }
    101 // Node* &rt   是传进来的root的一个引用,即它跟root是一个指针,只是一个别名
    102 void BSTree::insert(int k, Node * &rt)
    103 {
    104     //空树的情况
    105     if(rt==NULL)
    106     {
    107         // 这个new返回是什么? 应该就是一个指针,因为必须赋值给指针
    108         Node* nd = new Node(k,NULL,NULL,NULL);
    109         rt=nd;
    110         return;
    111     }
    112     if(rt->key>k)
    113     {
    114         insert(k,rt->lchild);
    115     }
    116     else
    117     {
    118         insert(k,rt->rchild);
    119     }
    120 
    121 }
    122 //注意所有调用dele函数时,参数都必须是传进来的参数t的某个直接相关元素
    123 //否则无法操作
    124 // 实际上 insert函数也有这个性质
    125 void BSTree::dele(int k, Node *&t)
    126 {
    127     if(t==NULL)
    128     {
    129         cout<<"Can't find"<<endl;
    130         return;
    131     }
    132     if(t->key==k)
    133     {
    134         if((t->lchild!=NULL)&&(t->rchild!=NULL))
    135         {
    136             Node* nd = successor(t);
    137             t->key = nd->key;
    138             dele(nd->key,t->rchild);
    139             //dele(nd->key,nd); 这样写是错误的!
    140         }
    141         else{
    142             t= (t->lchild!=NULL)?t->lchild:t->rchild;
    143 
    144         }
    145     }
    146     else if(t->key>k)
    147     {
    148         dele(k,t->lchild);
    149     }
    150     else dele(k,t->rchild);
    151 }
    152 
    153 
    154 
    155 int main()
    156 {
    157     srand(time(0));
    158     int keys[10],i;
    159     for(i=0;i<10;i++)
    160         keys[i]=random(500);
    161     BSTree* tree = new BSTree(NULL);
    162     for(i=0;i<8;i++)
    163     {
    164         tree->insert(keys[i],tree->root);
    165     }
    166     tree->inorderwalk(tree->root);
    167         cout<<endl;
    168     Node* n = tree->findmax(tree->root);
    169     cout<<n->key<<endl;
    170     n = tree->findmin(tree->root);
    171     Node* n2=tree->successor(n);
    172     cout<<n2->key<<endl;
    173     tree->dele(keys[4],tree->root);
    174     tree->inorderwalk(tree->root);
    175     cout<<endl;
    176 
    177 
    178 
    179 
    180     return 0;
    181 }
    BinarySearchTree
  • 相关阅读:
    Nginx详解
    MySQL数据库(未完)
    PXE自动装机
    Rsync数据同步服务
    NFS网络文件系统
    inotify事件监控工具
    scp ssh-key连接原理
    生产环境ssh登陆策略
    npm ERR! errno -4048
    js判断手机系统语言动态引入不同js文件
  • 原文地址:https://www.cnblogs.com/soyscut/p/3410129.html
Copyright © 2011-2022 走看看