经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C++ » 查看文章
平衡二叉树
来源:cnblogs  作者:xhqxhq  时间:2018/12/17 9:47:30  对本文有异议

1.平衡二叉树定义:如果一棵树不为空,其任意节点的左子树高度与右子树高度之差不超过1,那么满足这样条件的树就是平衡二叉树

2.平衡二叉树节点定义:

  1. 1 template<typename T>
  2. 2 struct tagNode
  3. 3 {
  4. 4 T m_element; //树节点所存储的元素节点
  5. 5 struct tagNode<T> * m_pLeftNode; //指向左孩子节点
  6. 6 struct tagNode<T> * m_pRightNode; //指向右孩子节点
  7. 7 struct tagNode<T> * m_pParentNode; //指向父节点
  8. 8 int m_nHeightOfNode;//节点高度
  9. 9 tagNode(T value,
  10. 10 tagNode<T> * pLeftNode = nullptr,
  11. 11 tagNode<T> *pRightNode = nullptr,
  12. 12 tagNode<T> *pParentNode = nullptr);
  13. 13 };
  14. 14
  15. 15 template<typename T>
  16. 16 tagNode<T>::tagNode(T value,
  17. 17 tagNode<T> * pLeftNode,
  18. 18 tagNode<T> *pRightNode,
  19. 19 tagNode<T> *pParentNode):
  20. 20 m_element(value),
  21. 21 m_pLeftNode(pLeftNode),
  22. 22 m_pRightNode(pRightNode),
  23. 23 m_pParentNode(pParentNode),
  24. 24 m_nHeightOfNode(0)
  25. 25 {
  26. 26
  27. 27 }
  28. 28
  29. 29
  30. 30 template<typename T>
  31. 31 using BINNODE = tagNode<T>;
  32. 32
  33. 33 template<typename T>
  34. 34 using PBINNODE = tagNode<T>*;

   这里平衡二叉树节点定义使用了模版,这样就可以任意自定义节点,using BINNODE = tagNode<T>则是为节点类型起一个别名,

  using PBINNODE = tagNode<T>*则使为该节点类型的指针起一个别名,因为该节点类型的定义使用了模版,

  所以只能用using关键字来起别名,不能使用typedef来起别名,有点扯远了。。。。

3.平衡二叉树的遍历:

  这里限定左子树比右子树优先遍历,所以一共有三种遍历:先序遍历,中序遍历,后序遍历

    

    下面将结合图3-1,来解释这三种遍历方式

    先序遍历:访问当前的根节点节点,然后遍历左子树,如果左子树为空则遍历右子树,

         如果右子树为空,则向上寻找到一个可以继续向右遍历的节点重复该过程,

         直到所有节点遍历完成

               对于上图,先序遍历的结果是:4->2->1->3->9->7->5->8->10->12

    下面分别给出先序遍历的递归实现和非递归实现:

     递归先序遍历:

  1. 1 //************************************************************************
  2. 2 // 函数名称: CBinTree<T>::PreOrderRecuursiveTraversal
  3. 3 // 函数功能: 先序遍历二叉树(递归实现)
  4. 4 // 所属权限: public
  5. 5 // 返回的值: bool
  6. 6 // 函数参数: CTraversalFunction<T> & FuncOfTravs:访问节点时使用的函数对象
  7. 7 // 函数参数: PBINNODE<T> pTravsralPos:遍历位置
  8. 8 // 注意事项:
  9. 9 //************************************************************************
  10. 10 template<typename T>
  11. 11 bool CBinTree<T>::PreOrderRecuursiveTraversal(CTraversalFunction<T> & FuncOfTravs, PBINNODE<T> pTravsralPos)
  12. 12 {
  13. 13 if (pTravsralPos != nullptr)
  14. 14 {
  15. 15 FuncOfTravs(pTravsralPos);
  16. 16 PreOrderRecuursiveTraversal(FuncOfTravs, pTravsralPos->m_pLeftNode);
  17. 17 PreOrderRecuursiveTraversal(FuncOfTravs, pTravsralPos->m_pRightNode);
  18. 18 }
  19. 19 return true;
  20. 20 }

 

  这里函数第一个参数FuncOfTravs是一个函数对象,其定义如下:

  1. //用于遍历时所用的函数对象,由子类实现函数调用运算符的重载,
  2. //在函数调用运算符中子类实现对节点进行如何操作
  3. template<typename T>
  4. class CTraversalFunction
  5. {
  6. public:
  7. virtual void operator()(PBINNODE<T> pNode) = 0;
  8. };

  顺便提下函数对象,好像是C++11标准才有的东西,当一个函数重载了函数调用运算符后,

  这个类产生的对象也称为为函数对象,例如在上面代码的第15行直接通过函数调用运算符调用函数,

  而无需通过FuncOfTravs.xxxx(....)的方式来调用成员函数,此时类对象FuncOfTravs仿佛就像一个函数一样。

  传统的C函数只能通过函数传参来携带信息,但是函数对象可以不用通过参数就能携带任意信息,

  这让我想起了Windows的线程回调函数,当我们使用CreateThread或者更安全的_beginThreadEx时,

  线程回调函数只能携带一个参数,所以一般都定义一个结构体,将这个结构体变量作为参数传递给线程回调函数,

  但是个人感觉函数对象携带参数的方式比这些回调函数更优雅些,嗯.......又扯远了。

  上述的函数调用操作符为我定义成纯虚函数,对于任何自定义的数据类型,只要继承

  该类模版,并重写函数调用运算符即可实现对自定义节点的遍历。

                   

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号