• 启迪思维:二叉树


    很多初级点的程序员会认为树结构无用论,也有初级程序员仅仅以为只有面试才会用到,还有自认为实际工作用不到(我身边工作好几年程序员懂树结构也没有几个),其实归根到底还是不清楚树的实际用途,下面分享我参加培训时候一个小尴尬。

     

    因为项目数据量很大(有很多表数据量都上亿的),对写sql能力要求很高,项目组会经常组织些数据库方面的培训,前段时间又参加公司一个SQL原理分析的一个培训,在培训中讲师问“为什么SQL走索引查询速度很快呢?”,我直接大声说“索引底层数据结构是B树,查询的时候用二分查找”,结果整个大房间就我一个人声音,所有同事都看过来,场面有点尴尬。

     

    上一篇文章分析树基本概念、名词解释、树三种遍历方式,今天继续来看二叉树各种名词概率,看这些名词概念,肯定是各种不爽,大概浏览下知道怎么回事就ok

     

    一:概念,下面这些内容摘自维基百科

    在计算机科学中,二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树常被用于实现二叉查找树和二叉堆。

    二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒。二叉树的第i层至多有个结点;深度为k的二叉树至多有个结点;对任何一棵二叉树T,如果其终端结点数为,度为2的结点数为,则。

    树和二叉树的三个主要差别:

    树的结点个数至少为1,而二叉树的结点个数可以为0

    树中结点的最大度数没有限制,而二叉树结点的最大度数为2

    树的结点无左、右之分,而二叉树的结点有左、右之分。

    完全二叉树和满二叉树

    满二叉树:一棵深度为k,且有个节点成为满二叉树

    完全二叉树:深度为k,有n个节点的二叉树,当且仅当其每一个节点都与深度为k的满二叉树中序号为1n的节点对应时,称之为完全二叉树

     

    二:示例图

     

    三:动画图

    四:代码分析

     1、是否为空

    1 /**
    2  * 若树为空,则返回true;否则返回false
    3  */
    4 bool IsEmpty() {
    5     return root == 0;
    6 }

    2、计算树的深度

     1 /**
     2  * 以传入节点为基础计算树的深度
     3  */
     4 int GetTreeDept(const TNode<T> *t) {
     5     int i, j;
     6     if (t == 0) {
     7         return 0;
     8     } else {
     9         //递归计算左子树的深度
    10         i = this->GetTreeDept(t->lchild);
    11         //递归计算右子树的深度
    12         j = this->GetTreeDept(t->rchild);
    13     }
    14 
    15     //t的深度为其左右子树中深度中的大者加1
    16     return i > j ? i + 1 : j + 1;
    17 }

    3、清空树的节点

     1 /**
     2  *清空树的所有节点
     3  */
     4 void Clear() {
     5     Clear(root);
     6 }
     7 
     8 /**
     9  *根据节点递归清空树
    10  */
    11 void Clear(TNode<T>* t) {
    12     //判断指针是否为空
    13     if (t) {
    14         //获取资源立即放入管理对象(参考Effective C++里边条款13)
    15         //tr1::shared_ptr(引用计数智慧指针)管理对象比auto_ptr更强大
    16         std::auto_ptr<TNode<T> > new_ptr(t);
    17 
    18         //递归清空右子树
    19         Clear(new_ptr->rchild);
    20 
    21         //递归清空左子树
    22         Clear(new_ptr->lchild);
    23     }
    24 
    25     //清空树的根节点
    26     t = 0;
    27 }

    4、获取树最大节点和最小节点

     1 /**
     2  *获取树的最大节点
     3  */
     4 TNode<T>* GetMax(TNode<T>* t) const {
     5     //判断数节点是否为空
     6     if (t) {
     7         //根据二叉树特性,最大值一定在右子树;
     8         //循环右子树,直到叶子节点
     9         while (t->rchild) {
    10             //指向下一个节点
    11             t = t->rchild;
    12         }
    13     }
    14     //返回找到最大节点
    15     return t;
    16 }
    17 /**
    18  *获取树的最小节点
    19  */
    20 TNode<T>* GetMin(TNode<T>* t) const {
    21     //判断数节点是否为空
    22     if (t) {
    23         //根据二叉树特性,最大值一定在左子树;
    24         //循环左子树,直到叶子节点
    25         while (t->lchild) {
    26             //指向下一个节点
    27             t = t->lchild;
    28         }
    29     }
    30     //返回找到最小节点
    31     return t;
    32 }
    33 /**
    34  *根据模式类型查找树的最大值或者最小值
    35  */
    36 TNode<T>* GetNode(Mode mode) const {
    37     //t指向根节点
    38     TNode<T>* t = root;
    39     //判断数节点是否为空
    40     if (t) {
    41         if (mode == Min) {
    42             //根据二叉树特性,最大值一定在左子树;
    43             //循环左子树,直到叶子节点
    44             while (t->lchild) {
    45                 //指向左子树下一个节点
    46                 t = t->lchild;
    47             }
    48         } else if (mode == Max) {
    49             //根据二叉树特性,最大值一定在右子树;
    50             //循环右子树,直到叶子节点
    51             while (t->rchild) {
    52                 //指向右子树下一个节点
    53                 t = t->rchild;
    54             }
    55         }
    56     }
    57     //返回找到节点
    58     return t;
    59 }

    5、获取传入节点父节点

     1 /**
     2  *获取传入的节点从传入树p中找到它的父节点
     3  */
     4 TNode<T>* GetParentNode(TNode<T> *p, const T &value,TNode<T> *returnValue) {
     5     //p节点存在并且传入的值不是根节点值
     6     if (p && p->data == value) {
     7         return 0;
     8     }
     9 
    10     //用二分查找定位值value所在节点
    11     TNode<T> *t = this->SearchTree(p, value);
    12 
    13     //判断t和p都不为空
    14     if (t && p) {
    15         //如果value的节点等于p节点左孩子或者右孩子,p就是value的节点父亲
    16         if (p->lchild == t || p->rchild == t) {
    17             //赋值p节点给返回值变量
    18             returnValue = p;
    19         } else if (value > p->data) {//如果value只大于p节点值,则递归右孩子
    20             //直到找到value的父节点复制给返回值变量
    21             returnValue = GetParentNode(p->rchild, value,returnValue);
    22         } else {////如果value只小于p节点值,则递归左孩子
    23             //直到找到value的父节点复制给返回值变量
    24             returnValue = GetParentNode(p->lchild, value,returnValue);
    25         }
    26     }
    27 
    28     return returnValue;
    29 
    30 }
    31 
    32 /**
    33  *获取传入的节点的父节点
    34  */
    35 TNode<T>* GetParentNode(const T &value,TNode<T> *returnValue) {
    36     return this->GetParentNode(root, value,returnValue);
    37 }

    6、二分查找

    代码分析:

     1 /**
     2  * 在以T为根节点的树中搜索值为value的节点
     3  */
     4 TNode<T>* SearchTree(TNode<T>* &t, const T &value) {
     5     //判断t节点是否为空
     6     while (t) {
     7         //如果节点值等于value,则表明已经找到目标节点
     8         if (t->data == value) {
     9             return t;
    10         } else if (value > t->data) {//如果value大于t节点值,则递归查询右子树
    11             return SearchTree(t->rchild, value);
    12         } else {//如果value小于t节点值,则递归查询左子树
    13             return SearchTree(t->lchild, value);
    14         }
    15     }
    16     return t;
    17 }

     动画演示:

    7、插入节点

    代码分析:

     1 /**
     2  *插入一个节点到目标树中
     3  */
     4 void Insert(const T &value, TNode<T>* &t) {
     5     //如果目标树为空,则新new一个根节点
     6     if (t == 0) {
     7         //新创建一个节点,并把value设置为节点值
     8         t = new TNode<T>(value);
     9     } else if (value < t->data) {//如果value值小于t节点值
    10         //递归左子树插入函数,直到找到节点插入
    11         this->Insert(value, t->lchild);
    12     } else if (value > t->data) {//如果value值大于t节点值
    13         //递归右子树插入函数,直到找到节点插入
    14         this->Insert(value, t->rchild);
    15     }
    16 }
    17 /**
    18  *插入一个节点到根节点为root的树中
    19  */
    20 void Insert(const T &value) {
    21     this->Insert(value, root);
    22 }

    动画演示

    8、删除节点

    代码分析:

      1 /**
      2  *根据节点值删除节点信息
      3  */
      4 void Delete(const T &value) {
      5     Delete(value, root);
      6 }
      7 /**
      8  *根据节点值删除以传入t为根节点树节点信息
      9  */
     10 void Delete(const T &value, TNode<T>* &t) {
     11     //判断是否t为空null
     12     if (t) {
     13         //通过二分查找定位value所在的节点
     14         TNode<T> *p = this->SearchTree(t, value);
     15         //中间变量,用于待删除节点左右子树都不为空的情况下
     16         TNode<T> *q = p;
     17         if (p) {
     18             //如果p节点的左右孩子都不为空,则根据二叉树定义
     19             //必须在子右子树中找到最新节点作为新节点
     20             //当左右子树都为空情况下,右子树最小节点就是树(中序遍历)节点的后继节点
     21             if (p->lchild != 0 && p->rchild != 0) {
     22                 //获取右子树中最小的节点
     23                 q = this->GetMin(p->rchild);
     24             }
     25             //获取资源立即放入管理对象(参考Effective C++里边条款13)
     26             //tr1::shared_ptr(引用计数智慧指针)管理对象比auto_ptr更强大
     27             //如果p节点的左右子树都不为空,则释放p节点子右子树的最小节点
     28             //改变p节点的值即可
     29             auto_ptr<TNode<T> > new_ptr(q);
     30 
     31             TNode<T> *parent = 0;
     32             TNode<T> *returnValue;
     33             //删除叶子节点(节点左右孩子都为空)
     34             if (p->lchild == 0 && p->rchild == 0) {
     35                 //如果p节点和传入的根节点相等
     36                 if (t == p) {
     37                     //直接设置t为空
     38                     t = 0;
     39                 } else {
     40                     //获取p节点的父节点
     41                     parent = this->GetParentNode(t, p->data,returnValue);
     42 
     43                     //如果父节点的左孩子等于p节点
     44                     if (parent->lchild == p) {
     45                         //设置父节点的左孩子等于空
     46                         parent->lchild = 0;
     47                     } else {//如果父节点的右孩子等于p节点
     48                         //设置父节点的右孩子等于空
     49                         parent->rchild = 0;
     50                     }
     51                 }
     52 
     53             } else if (p->rchild == 0) {//删除节点p右孩子为空,左孩子有节点
     54                 //如果p节点和传入的根节点相等
     55                 if (t == p) {
     56                     //直接设置t节点等于左孩子
     57                     t = t->lchild;
     58                 } else {
     59                     //获取p节点的父节点
     60                     parent = this->GetParentNode(t, p->data,returnValue);
     61                     //如果父节点的左孩子等于p节点
     62                     if (parent->lchild == p) {
     63                         //设置父节点左孩子等于p节点左孩子
     64                         parent->lchild = p->lchild;
     65                     } else {//如果父节点的右孩子等于p节点
     66                         //设置父节点右孩子等于p节点左孩子
     67                         parent->rchild = p->lchild;
     68                     }
     69                 }
     70 
     71             } else if (p->lchild == 0) {//删除节点p左孩子为空,右孩子有节点
     72                 //如果p节点和传入的根节点相等
     73                 if (t == p) {
     74                     //直接设置t节点等于右孩子
     75                     t = t->rchild;
     76                 } else {
     77                     //获取p节点的父节点
     78                     parent = this->GetParentNode(t, p->data,returnValue);
     79                     //如果父节点的右孩子等于p节点
     80                     if (parent->rchild == p) {
     81                         //设置父节点右孩子等于p节点右孩子
     82                         parent->rchild = p->rchild;
     83                     } else {//如果父节点的左孩子等于p节点
     84                         //设置父节点右孩子等于p节点右孩子
     85                         parent->lchild = p->rchild;
     86                     }
     87                 }
     88             } else {//删除节点p左右都有孩子
     89                 //获取q节点的父节点
     90                 parent = this->GetParentNode(t,q->data,returnValue);
     91                 //设置p节点值等于q节点值
     92                 p->data = q->data;
     93                 //如果q的父节点等于p
     94                 if (parent == p) {
     95                     //设置q节点的父节点右孩子为q节点右孩子
     96                     parent->rchild = q->rchild;
     97                 } else {//
     98                     //设置q节点的父节点左孩子为q节点右孩子
     99                     parent->lchild = q->rchild;
    100                 }
    101 
    102             }
    103         }
    104     }
    105 }

    动画演示

    9、获取目标节点后继节点(中序遍历) 

     1 /**
     2  *在传入p的树中找出节点值为value的后继节点方法
     3  */
     4 TNode<T>* TreeSuccessor(TNode<T> *p,const T &value,TNode<T> *returnValue){
     5     //如果t节点非空
     6     if(p){
     7         //传入p树和节点值value找到对应的节点
     8         TNode<T> *t = this->SearchTree(p, value);
     9         //如果节点右子树不为空
    10         if(t->rchild != 0){
    11             //直接获取右子树中最小节点,即是节点的后继节点
    12             returnValue = this->GetMin(t->rchild);
    13         }else{
    14             //获取目标节点父节点
    15             TNode<T> *parent = this->GetParentNode(t->data,returnValue);
    16 
    17             //如果父节点不为空并且t节点等于父节点的右节点,这一个文字不太好描述,请参照图
    18             while(parent && t == parent->rchild){
    19                 //父节点赋值给t节点
    20                 t = parent;
    21                 //获取父节点的父节点(目标节点爷爷)
    22                 parent = this->GetParentNode(parent->data,returnValue);
    23             }
    24             //找到后继节点赋值给返回变量
    25             returnValue = parent;
    26         }
    27     }
    28 
    29     return returnValue;
    30 }
    31 /**
    32  *在以root为根节点中找出节点值为value的后继节点方法
    33  */
    34 TNode<T>* TreeSuccessor(const T &value,TNode<T> *returnValue){
    35     return TreeSuccessor(root,value,returnValue);
    36 }

     如下图:

      

    10、先序、中序、后序递归和非的递归遍历,更详细的请参考上一篇文章,为什么在这里有展示一遍,我坚信在复杂的东西,多动手写几次都能很好的理解

      1 /**
      2  *前序非递归(利用栈)遍历二叉树
      3  *前序遍历的规则:根左右
      4  *非递归遍历树会经常当做面试题,考察面试者的编程能力
      5  *防止下次被鄙视,应该深入理解并且动手在纸写出来
      6  */
      7 void PreOrderTraverse() {
      8     //申明一个栈对象
      9     stack<TNode<T>*> s;
     10     //t首先指向根节点
     11     TNode<T> *t = root;
     12     //压入一个空指针,作为判断条件
     13     s.push(0);
     14 
     15     //如果t所值节点非空
     16     while (t != 0) {
     17         //直接访问根节点
     18         std::cout << (&t->data) << " ";
     19 
     20         //右孩子指针为非空
     21         if (t->rchild != 0) {
     22             //入栈右孩子指针
     23             s.push(t->rchild);
     24         }
     25 
     26         //左孩子指针为非空
     27         if (t->lchild != 0) {
     28             //直接指向其左孩子
     29             t = t->lchild;
     30         } else {//左孩子指针为空
     31             //获取栈顶元素(右孩子指针)
     32             t = s.top();
     33             //清楚栈顶元素
     34             s.pop();
     35         }
     36 
     37     }
     38 }
     39 
     40 /**
     41  *中序非递归(利用栈)遍历二叉树
     42  *前序遍历的规则:左根右
     43  */
     44 void InOrderTraverse() {
     45     //申明一个栈对象
     46     stack<TNode<T>*> s;
     47     //t首先指向根节点
     48     TNode<T>* t = root;
     49 
     50     //节点不为空或者栈对象不为空,都进入循环
     51     while (t != 0 || !s.empty()) {
     52         //如果t节点非空
     53         if (t) {
     54             //入栈t节点
     55             s.push(t);
     56             //t节点指向其左孩子
     57             t = t->lchild;
     58         } else {
     59             //获取栈顶元素(左孩子指针)
     60             t = s.top();
     61             //清楚栈顶元素
     62             s.pop();
     63             //直接访问t节点
     64             std::cout << (&t->data) << " ";
     65             //t节点指向其右孩子
     66             t = t->rchild;
     67         }
     68     }
     69 }
     70 /**
     71  *后序非递归(利用栈)遍历二叉树
     72  *前序遍历的规则:左右根
     73  */
     74 void PostOrderTraverse() {
     75     //申明一个栈对象
     76     stack<TNode<T>*> s;
     77     //t首先指向根节点
     78     TNode<T>* t = root;
     79     //申请中间变量,用做判断标识
     80     TNode<T>* r;
     81     //节点不为空或者栈对象不为空,都进入循环
     82     while (t != 0 || !s.empty()) {
     83         //如果t节点非空
     84         if (t) {
     85             //入栈t节点
     86             s.push(t);
     87             //t节点指向其左孩子
     88             t = t->lchild;
     89         } else {
     90             //获取栈顶元素(左孩子指针)
     91             t = s.top();
     92             //判断t的右子树是否存在并且没有访问过
     93             if (t->rchild && t->rchild != r) {
     94                 //t节点指向其右孩子
     95                 t = t->rchild;
     96                 //入栈t节点
     97                 s.push(t);
     98                 //t节点指向其左孩子
     99                 t = t->lchild;
    100             } else {
    101                 //获取栈顶元素(左孩子指针)
    102                 t = s.top();
    103                 //清楚栈顶元素
    104                 s.pop();
    105                 //直接访问t节点
    106                 std::cout << (&t->data) << " ";
    107                 //设置已经访问过的节点,防止多次访问(右孩子指针)
    108                 r = t;
    109                 t = 0;
    110             }
    111         }
    112     }
    113 }
    114 /**
    115  * 根据模式递归遍历二叉树
    116  * 下面代码相对比较简单,只要记住遍历树的规则并且弄一点递归,都可以写出来
    117  * 如果在面试中实在写不出来非递归方式,可以写一个递归版本,也许可以争取一个好的工作
    118  */
    119 void OrderTraverse(const TNode<T>* t, Style mode) {
    120     if (t) {
    121         //先序遍历二叉树:根左右
    122         if (mode == Pre) {
    123             //直接访问t节点
    124             std::cout << (&t->data) << " ";
    125             //递归遍历左子树
    126             this->OrderTraverse(t->lchild, mode);
    127             //递归遍历右子树
    128             this->OrderTraverse(t->rchild, mode);
    129         }
    130         //中序遍历二叉树:左根右
    131         if (mode == In) {
    132             //递归遍历左子树
    133             this->OrderTraverse(t->lchild, mode);
    134             //直接访问t节点
    135             std::cout << (&t->data) << " ";
    136             //递归遍历右子树
    137             this->OrderTraverse(t->rchild, mode);
    138         }
    139         //后序遍历二叉树:左右根
    140         if (mode == Post) {
    141             //递归遍历左子树
    142             this->OrderTraverse(t->lchild, mode);
    143             //递归遍历右子树
    144             this->OrderTraverse(t->rchild, mode);
    145             //直接访问t节点
    146             std::cout << (&t->data) << " ";
    147         }
    148     }
    149 }

    11、按层级遍历树

     1 /**
     2  *按层级从左到右遍历树
     3  */
     4 void LevelOrderTraverse(){
     5     //声明一个队列队形
     6     queue<TNode<T>* > q;
     7     //声明变量a,t;并把root赋值给t
     8     TNode<T> *a,*t = root;
     9     //若t节点非空
    10     if(t){
    11         //t节点入队列
    12         q.push(t);
    13         //如果队列不为空
    14         while(!q.empty()){
    15             //获取队列头结点
    16             a = q.front();
    17             //数据队形出队列
    18             q.pop();
    19             //直接访问队列头结点值
    20             std::cout<<a->data<<" ";
    21             //若a节点左子树不为空
    22             if(a->lchild){
    23                 //左子树入队列q
    24                 q.push(a->lchild);
    25             }
    26             //若a节点右子树不为空
    27             if(a->rchild){
    28                 //右子树入队列q
    29                 q.push(a->rchild);
    30             }
    31         }
    32     }
    33 }

    12、运行结果,由于在虚拟机中打中文,实在太痛苦,弄一点英文装下B 

    测试代码如下:

     1 void test() {
     2     Insert(5);
     3     Insert(3);
     4     Insert(4);
     5     Insert(6);
     6     Insert(2);
     7     Insert(1);
     8     Insert(10);
     9     Insert(9);
    10     Insert(8);
    11     Insert(11);
    12     Insert(12);
    13     std::cout << "create tree success" << std::endl;
    14 
    15     std::cout << "create tree after is null ? ";
    16     std::cout << boolalpha << this->IsEmpty();
    17 
    18     std::cout << std::endl;
    19     std::cout << "calculated depth of the tree begins" << std::endl;
    20     std::cout << "tree is dept = " << this->GetTreeDept(this->GetRoot());
    21     std::cout << std::endl;
    22     std::cout << "calculated depth of the tree end" << std::endl;
    23 
    24     std::cout << std::endl;
    25     std::cout << "recursion--------------------begin" << std::endl;
    26     std::cout << "pre order traverse:";
    27     OrderTraverse(GetRoot(), Pre);
    28     std::cout << endl;
    29 
    30     std::cout << "in order traverse:";
    31     OrderTraverse(GetRoot(), In);
    32     std::cout << endl;
    33 
    34     std::cout << "post order traverse:";
    35     OrderTraverse(GetRoot(), Post);
    36     std::cout << endl;
    37     std::cout << "recursion--------------------end" << std::endl;
    38 
    39     std::cout << "get parent node--------------begin" << std::endl;
    40     TNode<T> *returnValue;
    41     TNode<T> *node = GetParentNode(5,returnValue);
    42     if (node) {
    43         std::cout << "node=" << node->data;
    44     }
    45     std::cout << "get parent node--------------end" << std::endl;
    46     std::cout << "delete-----------------------begin" << std::endl;
    47     Delete(5);
    48     std::cout << "delete-----------------------end" << std::endl;
    49 
    50     std::cout << "recursion--------------------begin" << std::endl;
    51     std::cout << "in order traverse:";
    52     OrderTraverse(GetRoot(), In);
    53     std::cout << endl;
    54     std::cout << "recursion--------------------end" << std::endl;
    55     std::cout<<"tree max:"<<GetMax(GetRoot())->data<<std::endl;
    56     std::cout<<"tree min:"<<GetMin(GetRoot())->data<<std::endl;
    57 }
    View Code

    结果如下图

    11、完整代码

    TNode.h

     1 /*
     2  * TLNode.h
     3  *
     4  *  Created on: 2013-7-6
     5  *      Author: sunysen
     6  */
     7 
     8 #ifndef TLNODE_H_
     9 #define TLNODE_H_
    10 template <class T>
    11 class TNode{
    12 public:
    13     T data;
    14     TNode *rchild,*lchild;
    15     TNode(T value):data(value),rchild(0),lchild(0){}
    16 };
    17 
    18 #endif /* TLNODE_H_ */
    View Code

     BSTree.h

      1 /*
      2  * LinkTree.h
      3  *  Created on: 2013-7-6
      4  *      Author: sunysen
      5  */
      6 
      7 #ifndef BSTREE_H_
      8 #define BSTREE_H_
      9 #include "core/common/Common.h"
     10 #include "core/node/TNode.h"
     11 //获取树最大最小值模式
     12 enum Mode {
     13     Max, Min
     14 };
     15 //树的三种遍历方式
     16 enum ORDER_MODE {
     17     Pre, In, Post
     18 };
     19 template<class T>
     20 class BSTree {
     21 private:
     22     TNode<T> *root;//树的根节点
     23 public:
     24     /**
     25      * 构造函数初始化树根节点
     26      */
     27     BSTree() :
     28             root(0) {
     29     }
     30     /**
     31      *析构行数释放所有构造的资源
     32      */
     33     ~BSTree(){
     34         Clear();
     35     }
     36     /**
     37      * 若树为空,则返回true;否则返回false
     38      */
     39     bool IsEmpty() {
     40         return root == 0;
     41     }
     42 
     43     /**
     44      * 以传入节点为基础计算树的深度
     45      */
     46     int GetTreeDept(const TNode<T> *t) {
     47         int i, j;
     48         if (t == 0) {
     49             return 0;
     50         } else {
     51             //递归计算左子树的深度
     52             i = this->GetTreeDept(t->lchild);
     53             //递归计算右子树的深度
     54             j = this->GetTreeDept(t->rchild);
     55         }
     56 
     57         //t的深度为其左右子树中深度中的大者加1
     58         return i > j ? i + 1 : j + 1;
     59     }
     60     /**
     61      *清空树的所有节点
     62      */
     63     void Clear() {
     64         Clear(root);
     65     }
     66 
     67     /**
     68      *根据节点递归清空树
     69      */
     70     void Clear(TNode<T>* t) {
     71         //判断指针是否为空
     72         if (t) {
     73             //获取资源立即放入管理对象(参考Effective C++里边条款13)
     74             //tr1::shared_ptr(引用计数智慧指针)管理对象比auto_ptr更强大
     75             std::auto_ptr<TNode<T> > new_ptr(t);
     76 
     77             //递归清空右子树
     78             Clear(new_ptr->rchild);
     79 
     80             //递归清空左子树
     81             Clear(new_ptr->lchild);
     82         }
     83 
     84         //清空树的根节点
     85         t = 0;
     86     }
     87 
     88     /**
     89      *获取树的最大节点
     90      */
     91     TNode<T>* GetMax(TNode<T>* t) const {
     92         //判断数节点是否为空
     93         if (t) {
     94             //根据二叉树特性,最大值一定在右子树;
     95             //循环右子树,直到叶子节点
     96             while (t->rchild) {
     97                 //指向下一个节点
     98                 t = t->rchild;
     99             }
    100         }
    101         //返回找到最大节点
    102         return t;
    103     }
    104     /**
    105      *获取树的最小节点
    106      */
    107     TNode<T>* GetMin(TNode<T>* t) const {
    108         //判断数节点是否为空
    109         if (t) {
    110             //根据二叉树特性,最大值一定在左子树;
    111             //循环左子树,直到叶子节点
    112             while (t->lchild) {
    113                 //指向下一个节点
    114                 t = t->lchild;
    115             }
    116         }
    117         //返回找到最小节点
    118         return t;
    119     }
    120     /**
    121      *根据模式类型查找树的最大值或者最小值
    122      */
    123     TNode<T>* GetNode(Mode mode) const {
    124         //t指向根节点
    125         TNode<T>* t = root;
    126         //判断数节点是否为空
    127         if (t) {
    128             if (mode == Min) {
    129                 //根据二叉树特性,最大值一定在左子树;
    130                 //循环左子树,直到叶子节点
    131                 while (t->lchild) {
    132                     //指向左子树下一个节点
    133                     t = t->lchild;
    134                 }
    135             } else if (mode == Max) {
    136                 //根据二叉树特性,最大值一定在右子树;
    137                 //循环右子树,直到叶子节点
    138                 while (t->rchild) {
    139                     //指向右子树下一个节点
    140                     t = t->rchild;
    141                 }
    142             }
    143         }
    144         //返回找到节点
    145         return t;
    146     }
    147     /**
    148      *获取传入的节点从传入树p中找到它的父节点
    149      */
    150     TNode<T>* GetParentNode(TNode<T> *p, const T &value,TNode<T> *returnValue) {
    151         //p节点存在并且传入的值不是根节点值
    152         if (p && p->data == value) {
    153             return 0;
    154         }
    155 
    156         //用二分查找定位值value所在节点
    157         TNode<T> *t = this->SearchTree(p, value);
    158 
    159         //判断t和p都不为空
    160         if (t && p) {
    161             //如果value的节点等于p节点左孩子或者右孩子,p就是value的节点父亲
    162             if (p->lchild == t || p->rchild == t) {
    163                 //赋值p节点给返回值变量
    164                 returnValue = p;
    165             } else if (value > p->data) {//如果value只大于p节点值,则递归右孩子
    166                 //直到找到value的父节点复制给返回值变量
    167                 returnValue = GetParentNode(p->rchild, value,returnValue);
    168             } else {////如果value只小于p节点值,则递归左孩子
    169                 //直到找到value的父节点复制给返回值变量
    170                 returnValue = GetParentNode(p->lchild, value,returnValue);
    171             }
    172         }
    173 
    174         return returnValue;
    175 
    176     }
    177 
    178     /**
    179      *获取传入的节点的父节点
    180      */
    181     TNode<T>* GetParentNode(const T &value,TNode<T> *returnValue) {
    182         return this->GetParentNode(root, value,returnValue);
    183     }
    184 
    185     /**
    186      * 在以T为根节点的树中搜索值为value的节点
    187      */
    188     TNode<T>* SearchTree(TNode<T>* &t, const T &value) {
    189         //判断t节点是否为空
    190         while (t) {
    191             //如果节点值等于value,则表明已经找到目标节点
    192             if (t->data == value) {
    193                 return t;
    194             } else if (value > t->data) {//如果value大于t节点值,则递归查询右子树
    195                 return SearchTree(t->rchild, value);
    196             } else {//如果value小于t节点值,则递归查询左子树
    197                 return SearchTree(t->lchild, value);
    198             }
    199         }
    200         return t;
    201     }
    202 
    203     /**
    204      *插入一个节点到目标树中
    205      */
    206     void Insert(const T &value, TNode<T>* &t) {
    207         //如果目标树为空,则新new一个根节点
    208         if (t == 0) {
    209             //新创建一个节点,并把value设置为节点值
    210             t = new TNode<T>(value);
    211         } else if (value < t->data) {//如果value值小于t节点值
    212             //递归左子树插入函数,直到找到节点插入
    213             this->Insert(value, t->lchild);
    214         } else if (value > t->data) {//如果value值大于t节点值
    215             //递归右子树插入函数,直到找到节点插入
    216             this->Insert(value, t->rchild);
    217         }
    218     }
    219     /**
    220      *插入一个节点到根节点为root的树中
    221      */
    222     void Insert(const T &value) {
    223         this->Insert(value, root);
    224     }
    225 
    226     /**
    227      *根据节点值删除节点信息
    228      */
    229     void Delete(const T &value) {
    230         Delete(value, root);
    231     }
    232     /**
    233      *根据节点值删除以传入t为根节点树节点信息
    234      */
    235     void Delete(const T &value, TNode<T>* &t) {
    236         //判断是否t为空null
    237         if (t) {
    238             //通过二分查找定位value所在的节点
    239             TNode<T> *p = this->SearchTree(t, value);
    240             //中间变量,用于待删除节点左右子树都不为空的情况下
    241             TNode<T> *q = p;
    242             if (p) {
    243                 //如果p节点的左右孩子都不为空,则根据二叉树定义
    244                 //必须在子右子树中找到最新节点作为新节点
    245                 if (p->lchild != 0 && p->rchild != 0) {
    246                     //获取右子树中最小的节点
    247                     q = this->GetMin(p->rchild);
    248                 }
    249                 //获取资源立即放入管理对象(参考Effective C++里边条款13)
    250                 //tr1::shared_ptr(引用计数智慧指针)管理对象比auto_ptr更强大
    251                 //如果p节点的左右子树都不为空,则释放p节点子右子树的最小节点
    252                 //改变p节点的值,即可删除节点
    253                 auto_ptr<TNode<T> > new_ptr(q);
    254 
    255                 TNode<T> *parent = 0;
    256                 TNode<T> *returnValue;
    257                 //删除叶子节点(节点左右孩子都为空)
    258                 if (p->lchild == 0 && p->rchild == 0) {
    259                     //如果p节点和传入的根节点相等
    260                     if (t == p) {
    261                         //直接设置t为空
    262                         t = 0;
    263                     } else {
    264                         //获取p节点的父节点
    265                         parent = this->GetParentNode(t, p->data,returnValue);
    266 
    267                         //如果父节点的左孩子等于p节点
    268                         if (parent->lchild == p) {
    269                             //设置父节点的左孩子等于空
    270                             parent->lchild = 0;
    271                         } else {//如果父节点的右孩子等于p节点
    272                             //设置父节点的右孩子等于空
    273                             parent->rchild = 0;
    274                         }
    275                     }
    276 
    277                 } else if (p->rchild == 0) {//删除节点p右孩子为空,左孩子有节点
    278                     //如果p节点和传入的根节点相等
    279                     if (t == p) {
    280                         //直接设置t节点等于左孩子
    281                         t = t->lchild;
    282                     } else {
    283                         //获取p节点的父节点
    284                         parent = this->GetParentNode(t, p->data,returnValue);
    285                         //如果父节点的左孩子等于p节点
    286                         if (parent->lchild == p) {
    287                             //设置父节点左孩子等于p节点左孩子
    288                             parent->lchild = p->lchild;
    289                         } else {//如果父节点的右孩子等于p节点
    290                             //设置父节点右孩子等于p节点左孩子
    291                             parent->rchild = p->lchild;
    292                         }
    293                     }
    294 
    295                 } else if (p->lchild == 0) {//删除节点p左孩子为空,右孩子有节点
    296                     //如果p节点和传入的根节点相等
    297                     if (t == p) {
    298                         //直接设置t节点等于右孩子
    299                         t = t->rchild;
    300                     } else {
    301                         //获取p节点的父节点
    302                         parent = this->GetParentNode(t, p->data,returnValue);
    303                         //如果父节点的右孩子等于p节点
    304                         if (parent->rchild == p) {
    305                             //设置父节点右孩子等于p节点右孩子
    306                             parent->rchild = p->rchild;
    307                         } else {//如果父节点的左孩子等于p节点
    308                             //设置父节点右孩子等于p节点右孩子
    309                             parent->lchild = p->rchild;
    310                         }
    311                     }
    312                 } else {//删除节点p左右都有孩子
    313                     //获取q节点的父节点
    314                     parent = this->GetParentNode(t,q->data,returnValue);
    315                     //设置p节点值等于q节点值
    316                     p->data = q->data;
    317                     //如果q的父节点等于p
    318                     if (parent == p) {
    319                         //设置q节点的父节点右孩子为q节点右孩子
    320                         parent->rchild = q->rchild;
    321                     } else {//
    322                         //设置q节点的父节点左孩子为q节点右孩子
    323                         parent->lchild = q->rchild;
    324                     }
    325 
    326                 }
    327             }
    328         }
    329     }
    330 
    331     /**
    332      *前序非递归(利用栈)遍历二叉树
    333      *前序遍历的规则:根左右
    334      *非递归遍历树会经常当做面试题,考察面试者的编程能力
    335      *防止下次被鄙视,应该深入理解并且动手在纸写出来
    336      */
    337     void PreOrderTraverse() {
    338         //申明一个栈对象
    339         stack<TNode<T>*> s;
    340         //t首先指向根节点
    341         TNode<T> *t = root;
    342         //压入一个空指针,作为判断条件
    343         s.push(0);
    344 
    345         //如果t所值节点非空
    346         while (t != 0) {
    347             //直接访问根节点
    348             std::cout << (&t->data) << " ";
    349 
    350             //右孩子指针为非空
    351             if (t->rchild != 0) {
    352                 //入栈右孩子指针
    353                 s.push(t->rchild);
    354             }
    355 
    356             //左孩子指针为非空
    357             if (t->lchild != 0) {
    358                 //直接指向其左孩子
    359                 t = t->lchild;
    360             } else {//左孩子指针为空
    361                 //获取栈顶元素(右孩子指针)
    362                 t = s.top();
    363                 //清楚栈顶元素
    364                 s.pop();
    365             }
    366 
    367         }
    368     }
    369 
    370     /**
    371      *中序非递归(利用栈)遍历二叉树
    372      *前序遍历的规则:左根右
    373      */
    374     void InOrderTraverse() {
    375         //申明一个栈对象
    376         stack<TNode<T>*> s;
    377         //t首先指向根节点
    378         TNode<T>* t = root;
    379 
    380         //节点不为空或者栈对象不为空,都进入循环
    381         while (t != 0 || !s.empty()) {
    382             //如果t节点非空
    383             if (t) {
    384                 //入栈t节点
    385                 s.push(t);
    386                 //t节点指向其左孩子
    387                 t = t->lchild;
    388             } else {
    389                 //获取栈顶元素(左孩子指针)
    390                 t = s.top();
    391                 //清楚栈顶元素
    392                 s.pop();
    393                 //直接访问t节点
    394                 std::cout << (&t->data) << " ";
    395                 //t节点指向其右孩子
    396                 t = t->rchild;
    397             }
    398         }
    399     }
    400     /**
    401      *后序非递归(利用栈)遍历二叉树
    402      *前序遍历的规则:左右根
    403      */
    404     void PostOrderTraverse() {
    405         //申明一个栈对象
    406         stack<TNode<T>*> s;
    407         //t首先指向根节点
    408         TNode<T>* t = root;
    409         //申请中间变量,用做判断标识
    410         TNode<T>* r;
    411         //节点不为空或者栈对象不为空,都进入循环
    412         while (t != 0 || !s.empty()) {
    413             //如果t节点非空
    414             if (t) {
    415                 //入栈t节点
    416                 s.push(t);
    417                 //t节点指向其左孩子
    418                 t = t->lchild;
    419             } else {
    420                 //获取栈顶元素(左孩子指针)
    421                 t = s.top();
    422                 //判断t的右子树是否存在并且没有访问过
    423                 if (t->rchild && t->rchild != r) {
    424                     //t节点指向其右孩子
    425                     t = t->rchild;
    426                     //入栈t节点
    427                     s.push(t);
    428                     //t节点指向其左孩子
    429                     t = t->lchild;
    430                 } else {
    431                     //获取栈顶元素(左孩子指针)
    432                     t = s.top();
    433                     //清楚栈顶元素
    434                     s.pop();
    435                     //直接访问t节点
    436                     std::cout << (&t->data) << " ";
    437                     //设置已经访问过的节点,防止多次访问(右孩子指针)
    438                     r = t;
    439                     t = 0;
    440                 }
    441             }
    442         }
    443     }
    444     /**
    445      * 根据模式递归遍历二叉树
    446      * 下面代码相对比较简单,只要记住遍历树的规则并且弄一点递归,都可以写出来
    447      * 如果在面试中实在写不出来非递归方式,可以写一个递归版本,也许可以争取一个好的工作
    448      */
    449     void OrderTraverse(const TNode<T>* t, Style mode) {
    450         if (t) {
    451             //先序遍历二叉树:根左右
    452             if (mode == Pre) {
    453                 //直接访问t节点
    454                 std::cout << (&t->data) << " ";
    455                 //递归遍历左子树
    456                 this->OrderTraverse(t->lchild, mode);
    457                 //递归遍历右子树
    458                 this->OrderTraverse(t->rchild, mode);
    459             }
    460             //中序遍历二叉树:左根右
    461             if (mode == In) {
    462                 //递归遍历左子树
    463                 this->OrderTraverse(t->lchild, mode);
    464                 //直接访问t节点
    465                 std::cout << (&t->data) << " ";
    466                 //递归遍历右子树
    467                 this->OrderTraverse(t->rchild, mode);
    468             }
    469             //后序遍历二叉树:左右根
    470             if (mode == Post) {
    471                 //递归遍历左子树
    472                 this->OrderTraverse(t->lchild, mode);
    473                 //递归遍历右子树
    474                 this->OrderTraverse(t->rchild, mode);
    475                 //直接访问t节点
    476                 std::cout << (&t->data) << " ";
    477             }
    478         }
    479     }
    480 
    481 
    482     TNode<T>* GetRoot() {
    483         return root;
    484     }
    485 
    486     void test() {
    487         Insert(5);
    488         Insert(3);
    489         Insert(4);
    490         Insert(6);
    491         Insert(2);
    492         Insert(1);
    493         Insert(10);
    494         Insert(9);
    495         Insert(8);
    496         Insert(11);
    497         Insert(12);
    498         std::cout << "create tree success" << std::endl;
    499 
    500         std::cout << "create tree after is null ? ";
    501         std::cout << boolalpha << this->IsEmpty();
    502 
    503         std::cout << std::endl;
    504         std::cout << "calculated depth of the tree begins" << std::endl;
    505         std::cout << "tree is dept = " << this->GetTreeDept(this->GetRoot());
    506         std::cout << std::endl;
    507         std::cout << "calculated depth of the tree end" << std::endl;
    508 
    509         std::cout << std::endl;
    510         std::cout << "recursion--------------------begin" << std::endl;
    511         std::cout << "pre order traverse:";
    512         OrderTraverse(GetRoot(), Pre);
    513         std::cout << endl;
    514 
    515         std::cout << "in order traverse:";
    516         OrderTraverse(GetRoot(), In);
    517         std::cout << endl;
    518 
    519         std::cout << "post order traverse:";
    520         OrderTraverse(GetRoot(), Post);
    521         std::cout << endl;
    522         std::cout << "recursion--------------------end" << std::endl;
    523 
    524         std::cout << "get parent node--------------begin" << std::endl;
    525         TNode<T> *returnValue;
    526         TNode<T> *node = GetParentNode(5,returnValue);
    527         if (node) {
    528             std::cout << "node=" << node->data;
    529         }
    530         std::cout << "get parent node--------------end" << std::endl;
    531         std::cout << "delete-----------------------begin" << std::endl;
    532         Delete(5);
    533         std::cout << "delete-----------------------end" << std::endl;
    534 
    535         std::cout << "recursion--------------------begin" << std::endl;
    536         std::cout << "in order traverse:";
    537         OrderTraverse(GetRoot(), In);
    538         std::cout << endl;
    539         std::cout << "recursion--------------------end" << std::endl;
    540         std::cout<<"tree max:"<<GetMax(GetRoot())->data<<std::endl;
    541         std::cout<<"tree min:"<<GetMin(GetRoot())->data<<std::endl;
    542     }
    543 
    544 };
    545 
    546 #endif /* BSTREE_H_ */
    View Code

    五:环境

    1、运行环境:Ubuntu 10.04 LTS+VMware8.0.4+gcc4.4.3;

    2、开发工具:Eclipse+make

    六:题记

    1、上面的代码难免有bug,如果你发现代码写的有问题,请你帮忙指出,让我们一起进步,让代码变的更漂亮和更健壮;

    2、我自己能手动写上面代码,离不开郝斌、高一凡、侯捷、严蔚敏等老师的书籍和视频指导,在这里感谢他们;

    3、鼓励自己能坚持把更多数据结构方面的知识写出来,让自己掌握更深刻,也顺便冒充下"小牛";

     4、所有动画都在网上找的,感谢制作做动画的朋友,这样好的动画比图片更便于大家理解复杂的内容;

     

    欢迎继续阅读“启迪思维:数据结构和算法”系列

     

     

     

     

     

  • 相关阅读:
    mysql主从配置
    apache+mysql+php,安装整合配置。
    [转载]误将SELINUXTYPE看成SELINUX后,将其值改为disabled。导致操作系统服务启动,无法进入单用户模式
    python-趣味百题3
    python-趣味百题2
    python-趣味百题1
    python之路----1
    控件的textIsSelectable属性引起的血案
    导入eclipse项目 编码格式错误,运行包 不能映射的...编码
    Glide 缓存使用
  • 原文地址:https://www.cnblogs.com/sunysen/p/3225605.html
Copyright © 2020-2023  润新知