一、前言

《二叉查找树全面详细介绍》中讲解了二叉树操作:搜索(查找)、遍历、插入、删除。《二叉树遍历详解(递归遍历、非递归栈遍历,Morris遍历)》详细讲解了二叉树遍历的几种方法。《二叉树平衡(有序数组创建)》通过了一种构建平衡二叉树的方法

《二叉树平衡(有序数组创建)》讨论的算法的效率有点低,因为在创建完全平衡的树之前,需要使用一个额外的有序数组。为了避免排序,这一算法需要破坏树并用中序遍历把元素放在数组中,然后重建该树,这样做效率并不高,除非树很小。然而,存在几乎不需要存储中间变量也不需要排序过程的算法。Colin Day提出了非常简洁的DSW算法,Quentin F. Stout以及Bette L. Waren 对此算法进行了改进,本文讲解DSW算法构建平衡二叉树(完全平衡二叉树)。

二、知识点回顾

2.1 二叉树的旋转

有两种旋转方法:

(1)左旋转:将根节点旋转到(根节点的)右孩子的左孩子位置

(2)右旋转:将根节点旋转到(根节点的)左孩子的右孩子位置

下图为左、右旋转图,黄色节点为旋转的根节点,灰色节点是可以有也可以没有的节点。

三、DSW算法(Day–Stout–Warren algorithm)

一般来说,DSW算法首先将任意的二叉查找树转换为类似于链表的树,称为主链或主干。然后围绕主链中第二个节点的父节点,反复将其旋转,将这棵被拉伸的树在系列步骤中转换成完全平衡的树。DSW算法可以分为两个大的步骤。

步骤一:将二叉树右旋转形成主链;

步骤二:左旋转转换为平衡树;

其中步骤二可以分为两个阶段:

阶段一:左旋n-m次

阶段二:进入循环根据每一层节点数进行旋转。

3.1 步骤一

createBackbone (root)tmp = root;while (tmp !=0)if tmp有左子节点围绕tmp旋转该子节点: //这样该左子节点将成为tmp的父节点tmp设置为刚刚成为父节点的子节点;else 将tmp设置为它的右子节点:

步骤一实例演示:

3.2 步骤二

createPerfectTree ()n=节点数;m = (1 << (int)log2(n)) - 1; //计算当前节点数n与最接近完全平衡二叉树中节点数之间的差,多出的节点将单独处理// 第一阶段:进行左旋m-n次// 第二阶段:进入循环根据每一层节点数进行左旋转while (m> 1)m=m/2;从主链的顶部开始做m次旋转;

这里不要好理解的是,这两个阶段都是进行左旋转,为什么要分开进行?

个人理解:第一阶段需要先进行n-m次左旋转,是为了保证最后一排阶段是在树的最左侧。第二阶段,可以理解为根据树的层次中节点数量进行旋转次数。有点抽象不容易理解,看图,更直观些,容易加深理解。

1)第一阶段第一次旋转,对节点1进行向左旋转

2)第一阶段第二次旋转,旋转节点是上一次旋转节点的后面第2个节点也就是节点3作为本次旋转节点。

3)第一阶段的两次旋转完成

4)第二阶段第一层第一次旋转,以第一阶段旋转完的树根节点作为本次旋转节点。

第一层旋转次数3次, 3 = 7>>1。

5)第二阶段第一层第二次旋转,旋转节点是上一次旋转节点的后面第2个节点也就是节点5作为本次旋转节点。

6)第二阶段第一层第三次旋转,旋转节点是上一次旋转节点的后面第2个节点也就是节点8作为本次旋转节点。

7)第二阶段第一层第三次旋转完成。我们发现,当前生成的树是平衡二叉树,但不是完全平衡二叉树,其中有两个节点是在最后一层的右边。最后一层节点数量和步骤一中旋转次数是对应的。现在需要将其转换成完平衡二叉树,需要继续旋转。

8)第二阶段第二层第一次旋转,以上一层旋转完的树根节点作为本次旋转节点。

第二层旋转次数1次, 1 = 3>>1。

9)旋转完成,完全平衡二叉树构建成功。

四、总结

整体来说这种思路有些抽象,需要结合图多多领悟。时间复杂度和空间复杂度相比《二叉树平衡(有序数组创建)》好很多。

五、编码实现

//==========================================================================
/**
* @file    : DswBST.h
* @blogs   : https://blog.csdn.net/nie2314550441/article/details/107095634
* @author  : niebingyu
* @title   : DSW算法构建平衡二叉树
* @purpose : DSW算法构建平衡二叉树
*/
//==========================================================================
#pragma once
#include "GenBST.h"template<class T>
class DswBST : private BST<T>
{
public:DswBST(T* a, int len);    //根据数组中的数据构造树,调试测试用// 平衡二叉树void dswBalance();// 前序遍历二叉树void preorder(){ BST<T>::preorder(); }// 中序遍历二叉树void inorder() { BST<T>::inorder(); }protected:// 步骤一:将二叉树右旋转形成主链void createBackbone();// 步骤二:左旋转转换为平衡树void creatPerfectTree();// 右旋转void rotateRight(BSTNode<T>* Gr, BSTNode<T>* Par, BSTNode<T>* Ch);// 左旋转void rotateLeft(BSTNode<T>* Gr, BSTNode<T>* Par, BSTNode<T>* Ch);private:int m_count;
};template<class T>
DswBST<T>::DswBST(T* a, int len)
{m_count = len;for (int i = 0; i < len; i++) {this->insert(a[i]);}
}template<class T>
void DswBST<T>::dswBalance()
{createBackbone();creatPerfectTree();
}// 二叉查找树转化成主链的过程分析
/**********************************************************************************************
*  5 <-tmp         5               5               5              5
*   \               \               \               \               \
*    10             10              10              10              10
*      \              \               \               \               \
*       20            15              15              15              15
*      /  \             \               \               \               \
*     15  30            20              20              20              20
*         / \             \              \                \               \
*        25 40            30 <-tmp       25 <-tmp         23               23
*       /  \             /  \           /  \               \                \
*     23    28          25   40        23   30              25              25
*                      /  \                /  \              \                \
*                     23   28             28   40            30 <-tmp         28
*                                                           /  \               \
*                                                          28  40               30
*                                                                                \
*                                                                                 40 <-tmp
***********************************************************************************************/
// 步骤一:将二叉树右旋转形成主链
template<class T>
void DswBST<T>::createBackbone()
{BSTNode<T>* Gr = 0, *Par = this->m_root, *Ch = 0;while (Par != 0) {Ch = Par->m_left;if (Ch != 0) {rotateRight(Gr, Par, Ch);Par = Ch;}else {Gr = Par;Par = Par->m_right;}// 旋转过程中,如果是绕根节点的右节点旋转时要将根节点置为原根节点的右节点if (Gr == 0)this->m_root = Ch;}
}/*************************************************************************  子节点Ch围绕父节点Par的右旋转*   Before      After*    Gr          Gr*     \           \*     Par         Ch*    /  \        /  \*   Ch   Z      X   Par*  /  \            /  \* X    Y          Y    Z***********************************************************************/
// 右旋转
template<class T>
void DswBST<T>::rotateRight(BSTNode<T>* Gr, BSTNode<T>* Par, BSTNode<T>* Ch)
{if (Gr != 0)Gr->m_right = Ch;Par->m_left = Ch->m_right;Ch->m_right = Par;
}// 左旋转
template<class T>
void DswBST<T>::rotateLeft(BSTNode<T>* Gr, BSTNode<T>* Par, BSTNode<T>* Ch)
{if (Gr != 0)Gr->m_right = Ch;Par->m_right = Ch->m_left;Ch->m_left = Par;
}// 步骤二:左旋转转换为平衡树
template<class T>
void DswBST<T>::creatPerfectTree()
{int n = m_count;if (n < 3)return; //节点数目小于3不用平衡int m = (1 << (int)log2(n)) - 1;BSTNode<T>* Gr = 0;BSTNode<T>* Par = this->m_root;BSTNode<T>* Ch = this->m_root->m_right;this->m_root = this->m_root->m_right; //修改root指针// 第一阶段: 左旋n-m次for (int i = 0; i < n - m; i++) {rotateLeft(Gr, Par, Ch);Gr = Ch;Par = Gr->m_right;if (0 != Par) Ch = Par->m_right;else break;}// 第二阶段,进入while循环while (m > 1) {m = m >> 1;BSTNode<T>* Gr = 0;BSTNode<T>* Par = this->m_root;BSTNode<T>* Ch = this->m_root->m_right;this->m_root = this->m_root->m_right;for (int i = 0; i < m; i++) {rotateLeft(Gr, Par, Ch);Gr = Ch;Par = Gr->m_right;if (0 != Par) Ch = Par->m_right;else break;}}
}

测试代码

//==========================================================================
/**
* @file    : DswBSTTest.h
* @blogs   :
* @author  : niebingyu
* @title   : 测试DSW算法构建平衡二叉树
* @purpose : 测试DSW算法构建平衡二叉树
*
*/
//==========================================================================
#pragma once
#include "DswBST.h"
using namespace std;#define NAMESPACE_DSWBSTTEST namespace NAME_DSWBSTTEST {
#define NAMESPACE_DSWBSTTESTEND }
NAMESPACE_DSWBSTTEST// 测试用例
void Test1()
{vector<int> data = { 1,2,3,4,5,6,7,8,9};DswBST<int> tree(data.data(), data.size());tree.dswBalance();cout << "Test1 前序遍历: ";tree.preorder();cout << "\nTest1 中序遍历: ";tree.inorder();cout << endl;
}void Test2()
{vector<int> data = { 5, 10, 20, 15, 30, 25, 40, 23, 28 };DswBST<int> tree(data.data(), data.size());tree.dswBalance();cout << "Test2 前序遍历: ";tree.preorder();cout << "\nTest2 中序遍历: ";tree.inorder();cout << endl;
}// 测试用例
void Test3()
{vector<int> data;for (int i = 1; i <= 33; ++i)data.push_back(i);DswBST<int> tree(data.data(), data.size());tree.dswBalance();cout << "Test1 前序遍历: ";tree.preorder();cout << "\nTest1 中序遍历: ";tree.inorder();cout << endl;
}
NAMESPACE_DSWBSTTESTENDvoid DswBSTTest_Test()
{NAME_DSWBSTTEST::Test1();NAME_DSWBSTTEST::Test2();NAME_DSWBSTTEST::Test3();
}

执行结果:

二叉树平衡(DSW算法)相关推荐

  1. 怎样将树的中序遍历的数输入到一个数组中_数据结构与算法-二叉查找树平衡(DSW)...

    上一节探讨了二叉查找树的基本操作,二叉查找树的查找效率在理想状态下是O(lgn),使用该树进行查找总是比链表快得多.但是,该论点并不总是正确,因为查找效率和二叉树的形状息息相关.就像这样: 图1-1给 ...

  2. 数据算法之二叉树平衡(BinTreeNode Rotate)的Java实现

    本文的代码来自于<数据结构与算法(JAVA语言版)>,是笔者在网上找到的资料,非正式出刊版物.笔者对代码一些比较难以理解的部分添加了注释和图解,欢迎大家来讨论. 二叉树平衡的基本思想是通过 ...

  3. 【swjtu】数据结构实验6_二叉树的遍历算法

    实验内容及要求: 编写程序,用先序递归遍历法建立二叉树的二叉链表存储结构,然后输出其先序.中序.后序以及层次遍历结点访问次序.其中层次遍历的实现需使用循环队列.二叉树结点数据类型建议选用字符类型. 实 ...

  4. 重拾算法(3)——用458329个测试用例全面测试二叉树和线索二叉树的遍历算法

    重拾算法(3)--用458329个测试用例全面测试二叉树和线索二叉树的遍历算法 在"上一篇"和"上上一篇"中,我给出了二叉树和线索二叉树的遍历算法.给出算法容易 ...

  5. 二叉树的遍历(算法导论第三版12.1-4)(包含先序遍历,后序遍历和中序遍历)

    二叉树的遍历(算法导论第三版12.1-4) 1⃣️先序遍历 template<typename T> void preorder_tree_wald(BinaryTreeNode<T ...

  6. 基于小波精英解学习和多角度搜索的新型阴阳平衡优化算法

    文章目录 一.理论基础 1.基本阴阳平衡优化算法 (1)基于档案集的解更新阶段 (2)基于超球体的解更新阶段 (3)阴阳平衡优化(YYPO)算法伪代码 2.基于小波精英解学习和多角度搜索的新型阴阳平衡 ...

  7. 二叉树结构与算法思路解析

    二叉树 介绍 主要内容 二叉树的概念和性质 二叉树的存储结构 遍历二叉树 递归遍历 非递归遍历 线索二叉树 哈夫曼树 树和森林 树和森林的存储 树和森林与二叉树的转换 树和森林的遍历 树型结构特点 一 ...

  8. 二叉树树叶统计算法详解

    一.引言 二叉树树叶,就是度为零的结点,或者说左右子树都为空的结点就是树叶.要统计这些树叶的个数,也就是统计一下左右子树均为空的结点个数,因此只需要在二叉树上依次查看每一个结点的左右子树是否为空即可, ...

  9. 二叉树的基础算法综述

    二叉树是一种递归定义的数据结构,自然而然地其算法大多也都采用递归的形式.二叉树的基础算法包括且不限于: (1)二叉树的建立 (2)二叉树的三种遍历 (3)二叉树各类结点数计算(度为2,1,0的个数) ...

最新文章

  1. js用递归遍历多维数组_js面试题更新之40
  2. 《数据库SQL实战》获取当前(to_date='9999-01-01')薪水第二多的员工的emp_no以及其对应的薪水salary
  3. OpenCV 图像清晰度评价算法(相机自动对焦)
  4. python装饰器补充
  5. Flutter VS React Native –为什么我认为Flutter最适合移动应用程序开发
  6. 深入浅出.NET泛型编程(1)
  7. 华为p10 鸿蒙,全面上线!华为鸿蒙新消息传来,这是要彻底替换安卓
  8. Hash(除留余数法+链地址法)
  9. (转)WebApi发送HTML表单数据:文件上传与多部分MIME
  10. 系统学习NLP(六)--语义分析
  11. 统计学常用的数据分析方法总结
  12. [3D检测系列-PointRCNN]复现PointRCNN代码,并实现PointRCNN3D目标检测可视化,包含预训练权重下载链接(从0开始以及各种报错的解决方法)
  13. SpringBoot整合Validation进行参数校验
  14. Android开发笔记(一百八十一)使用CameraX拍照
  15. APP软件开发的步骤
  16. 沙漠下新雨,树木爆翠绿,没有放弃与更新,就不成其智慧。我们不必时刻刷新微博和朋友圈,比起这些,刷新自我更加重要。我们并不需要生活在别处,不需要流于表面,我们需要时常更新生命。
  17. kubernetes源码剖析读后感(二)
  18. 读写配置文件模块configparser—参考杨永明博客
  19. 卷妹带你学数据库---5天冲刺Day5
  20. 商汤科技2018校招研究员笔试第一场

热门文章

  1. layui后台框架的搭建
  2. 干货 | 携程旅行App iOS工程编译优化实践
  3. CS231n-assignment3-Generative Adversarial Networks (GANs)
  4. ACM之路怎么走(不搞竞赛也可以看看)
  5. 如何获取微信公众号openid
  6. IntelliJ IDEA 项目文件旁边都有0%classes,0% lines covered
  7. 分布式存储市场及发展趋势报告
  8. android studio 打包aar 与 引入aar包
  9. API_2 安装详细流程
  10. show函数的作用是什么python_python 学习之Python函数