平衡树以及AVL树

  平衡树是计算机科学中的一类数据结构。 平衡树是计算机科学中的一类改进的二叉查找树。一般的二叉查找树的查询复杂度是跟目标结点到树根的距离(即深度)有关,因此当结点的深度普遍较大时,查询的均摊复杂度会上升,为了更高效的查询,平衡树应运而生了。

  在这里,平衡指所有叶子的深度趋于平衡,更广义的是指在树上所有可能查找的均摊复杂度偏低。

  几乎所有平衡树的操作都基于树操作,通过旋转操作可以使得树趋于平衡。 对一棵查找树(search tree)进行查询/新增/删除 等动作, 所花的时间与树的高度h 成比例, 并不与树的容量 n 成比例。如果可以让树维持矮矮胖胖的好身材, 也就是让h维持在O(lg n)左右, 完成上述工作就很省时间。能够一直维持好身材, 不因新增删除而长歪的搜寻树, 叫做balanced search tree(平衡树)。 旋转Rotate —— 不破坏左小右大特性的小手术 平衡树有很多种, 其中有几类树维持平衡的方法, 都是靠整形小手术。

  各种平衡树:AVL树,经典平衡树,所有操作的最坏复杂度是O(lgN)的。

        Treap,利用随机堆的期望深度来优化树的深度,达到较优的期望复杂度。

        伸展树、红黑树,节点大小平衡树。2-3树、AA树。

 

AVL树:

  AVL树是一棵自平衡的二叉搜索树,在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下都是O(lgN)

 

为什么需要AVL树:

  大多数二叉查找操作(搜索、最大、最小、插入、删除...)会花费O(h),h是二叉搜索树的高度。对于不平衡的二叉查找树,这些操作的时间复杂度为O(n)。如果我们保证在每一次插入和删除之后树的高度为O(lgN),那么我们就能保证对于所有的操作都有O(lgN)的上界。AVL树的高度总是O(logN),n是树中节点的数量。

 

2. 旋转

如果在AVL树中进行插入或删除节点后,可能导致AVL树失去平衡。这种失去平衡的可以概括为4种姿态:LL(左左),LR(左右),RR(右右)和RL(右左)。下面给出它们的示意图:

上图中的4棵树都是"失去平衡的AVL树",从左往右的情况依次是:LL、LR、RL、RR。除了上面的情况之外,还有其它的失去平衡的AVL树,如下图:

上面的两张图都是为了便于理解,而列举的关于"失去平衡的AVL树"的例子。总的来说,AVL树失去平衡时的情况一定是LL、LR、RL、RR这4种之一,它们都由各自的定义:

(1) LL:LeftLeft,也称为"左左"。插入或删除一个节点后,根节点的左子树的左子树还有非空子节点,导致"根的左子树的高度"比"根的右子树的高度"大2,导致AVL树失去了平衡。
     例如,在上面LL情况中,由于"根节点(8)的左子树(4)的左子树(2)还有非空子节点",而"根节点(8)的右子树(12)没有子节点";导致"根节点(8)的左子树(4)高度"比"根节点(8)的右子树(12)"高2。

 

(2) LR:LeftRight,也称为"左右"。插入或删除一个节点后,根节点的左子树的右子树还有非空子节点,导致"根的左子树的高度"比"根的右子树的高度"大2,导致AVL树失去了平衡。
     例如,在上面LR情况中,由于"根节点(8)的左子树(4)的左子树(6)还有非空子节点",而"根节点(8)的右子树(12)没有子节点";导致"根节点(8)的左子树(4)高度"比"根节点(8)的右子树(12)"高2。

 

(3) RL:RightLeft,称为"右左"。插入或删除一个节点后,根节点的右子树的左子树还有非空子节点,导致"根的右子树的高度"比"根的左子树的高度"大2,导致AVL树失去了平衡。
     例如,在上面RL情况中,由于"根节点(8)的右子树(12)的左子树(10)还有非空子节点",而"根节点(8)的左子树(4)没有子节点";导致"根节点(8)的右子树(12)高度"比"根节点(8)的左子树(4)"高2。

 

(4) RR:RightRight,称为"右右"。插入或删除一个节点后,根节点的右子树的右子树还有非空子节点,导致"根的右子树的高度"比"根的左子树的高度"大2,导致AVL树失去了平衡。
     例如,在上面RR情况中,由于"根节点(8)的右子树(12)的右子树(14)还有非空子节点",而"根节点(8)的左子树(4)没有子节点";导致"根节点(8)的右子树(12)高度"比"根节点(8)的左子树(4)"高2。

 

前面说过,如果在AVL树中进行插入或删除节点后,可能导致AVL树失去平衡。AVL失去平衡之后,可以通过旋转使其恢复平衡,下面分别介绍"LL(左左),LR(左右),RR(右右)和RL(右左)"这4种情况对应的旋转方法。

 

2.1 LL的旋转

LL失去平衡的情况,可以通过一次旋转让AVL树恢复平衡。如下图:

图中左边是旋转之前的树,右边是旋转之后的树。从中可以发现,旋转之后的树又变成了AVL树,而且该旋转只需要一次即可完成。
对于LL旋转,你可以这样理解为:LL旋转是围绕"失去平衡的AVL根节点"进行的,也就是节点k2;而且由于是LL情况,即左左情况,就用手抓着"左孩子,即k1"使劲摇。将k1变成根节点,k2变成k1的右子树,"k1的右子树"变成"k2的左子树"。

 

LL的旋转代码

1 template<typename T>
2 void AVLTree<T>::RotateWithLeftChild(AVLTreeNode* &z) {
3     AVLTreeNode* y = z->left;
4     z->left = y->right;
5     y->right = z;
6     z->height = max(GetHeight(z->left), GetHeight(z->right)) + 1;
7     y->right = max(GetHeight(y->left), z->height)) + 1;
8     z = y;
9 }

 

 

2.2 RR的旋转

理解了LL之后,RR就相当容易理解了。RR是与LL对称的情况!RR恢复平衡的旋转方法如下:

图中左边是旋转之前的树,右边是旋转之后的树。RR旋转也只需要一次即可完成。

 

RR的旋转代码

1 template<typename T>
2 void AVLTree<T>::RotateWithRightChild(AVLTreeNode* &z) {
3     AVLTreeNode* y = z->right;
4     z->right = y->left;
5     y->left = z;
6     z->height = max(GetHeight(z->left), GetHeight(z->right)) + 1;
7     y->right = max(GetHeight(y->right), z->height)) + 1;
8     z = y;
9 }

 

 

2.3 LR的旋转

LR失去平衡的情况,需要经过两次旋转才能让AVL树恢复平衡。如下图:

第一次旋转是围绕"k1"进行的"RR旋转",第二次是围绕"k3"进行的"LL旋转"。

 

LR的旋转代码

1 template<typename T>
2 void AVLTree<T>::DoubleWithLeftChild(AVLTreeNode* &z) {
3     RotateWithRightChild(z->left);
4     RotateWithLeftChild(z);
5 }

 

 

 

2.4 RL的旋转

RL是与LR的对称情况!RL恢复平衡的旋转方法如下:

第一次旋转是围绕"k3"进行的"LL旋转",第二次是围绕"k1"进行的"RR旋转"。


RL的旋转代码

1 template<typename T>
2 void AVLTree<T>::DoubleWithRightChild(AVLTreeNode* &z) {
3     RotateWithRightChild(z->right);
4     RotateWithLeftChild(z);
5 }

 

插入:

  向AVL树插入,可以透过如同它是未平衡的二叉查找树一样,把给定的值插入树中,接着自底往上向根节点折回,于在插入期间成为不平衡的所有节点上进行旋转来完成。因为折回到根节点的路途上最多有1.44乘log n个节点,而每次AVL旋转都耗费固定的时间,所以插入处理在整体上的耗费为O(log n) 时间。

 

 删除:

  从AVL树中删除,可以通过把要删除的节点向下旋转成一个叶子节点,接着直接移除这个叶子节点来完成。因为在旋转成叶子节点期间最多有log n个节点被旋转,而每次AVL旋转耗费固定的时间,所以删除处理在整体上耗费O(log n) 时间。

 查找

  可以像普通二叉查找树一样的进行,所以耗费O(log n)时间,因为AVL树总是保持平衡的。不需要特殊的准备,树的结构不会由于查找而改变。

 

代码:

  头文件:

 1 #ifndef AVL_TREE_H_
 2 #define AVL_TREE_H_
 3 
 4 template<typename T>
 5 class AVLTree {
 6  public:
 7      AVLTree():root_(NULL){}
 8      AVLTree(const AVLTree &rhs){} 
 9      AVLTree& operator=(const AVLTree &rhs){}
10      ~AVLTree(){}
11 
12      void Insert(const T& k) {
13          Insert(root_, k);
14      }
15 
16      void Remove(const T& k) {
17          Remove(root_, k);
18      }
19 
20  private:
21      struct AVLTreeNode {
22          T key;
23          int height;
24          AVLTreeNode* left;
25          AVLTreeNode* right;
26 
27          AVLTreeNode(const T& k, AVLTreeNode* l = NULL, AVLTreeNode * r = NULL, int h = 0)
28              : key(k), left(l), right(r), height(h) {}
29      };
30 
31      AVLTreeNode *root_; //根节点
32 
33      int GetHeight(AVLTreeNode* p) const {
34          return p == NULL ? -1 : p->height;
35      }
36 
37      void Insert(AVLTreeNode* &p, const T& k);
38      void Remove(AVLTreeNode* &p, const T& k);
39 
40      void RotateWithLeftChild(AVLTreeNode* &z);
41      void RotateWithRightChild(AVLTreeNode* &z);
42 
43      void DoubleWithLeftChild(AVLTreeNode* &z);
44      void DoubleWithRightChild(AVLTreeNode* &z);
45 
46      AVLTreeNode* FindMin(AVLTreeNode* p) const;
47 };
48 #endif

 

源文件:

  

  1 #include "avl_tree.h"
  2 
  3 //LL
  4 template<typename T>
  5 void AVLTree<T>::RotateWithLeftChild(AVLTreeNode* &z) {
  6     AVLTreeNode* y = z->left;
  7     z->left = y->right;
  8     y->right = z;
  9     z->height = max(GetHeight(z->left), GetHeight(z->right)) + 1;
 10     y->right = max(GetHeight(y->left), z->height) + 1;
 11     z = y;
 12 }
 13 
 14 //RR
 15 template<typename T>
 16 void AVLTree<T>::RotateWithRightChild(AVLTreeNode* &z) {
 17     AVLTreeNode* y = z->right;
 18     z->right = y->left;
 19     y->left = z;
 20     z->height = max(GetHeight(z->left), GetHeight(z->right)) + 1;
 21     y->right = max(GetHeight(y->right), z->height) + 1;
 22     z = y;
 23 }
 24 
 25 //LR
 26 template<typename T>
 27 void AVLTree<T>::DoubleWithLeftChild(AVLTreeNode* &z) {
 28     RotateWithRightChild(z->left);
 29     RotateWithLeftChild(z);
 30 }
 31 
 32 //RL
 33 template<typename T>
 34 void AVLTree<T>::DoubleWithRightChild(AVLTreeNode* &z) {
 35     RotateWithRightChild(z->right);
 36     RotateWithLeftChild(z);
 37 }
 38 
 39 template<typename T>
 40 void AVLTree<T>::Insert(AVLTreeNode* &p, const T& k) {
 41     if (p == NULL) {
 42         t = new AVLTreeNode(k);
 43     } else if (k < p->key) { //左子树中插入
 44         Insert(p->left, k);
 45         if (GetHeight(p->left) - GetHeight(p->right) == 2) { //虽然每次都检查,但是只调整最后一次
 46             if (k < p->left->key) { //LL
 47                 RotateWithLeftChild(p);
 48             } else { //LR
 49                 DoubleWithLeftChild(p);
 50             }
 51         }
 52     } else if (k > p->val) {//在右子树中插入
 53         Insert(p->right, k);
 54         if (GetHeight(p->right) - GetHeight(p->left) == 2) {
 55             if (x > p->right->key) {//RR
 56                 RotateWithRightChild(p);
 57             } else { //RL
 58                 DoubleWithRightChild(p);
 59             }
 60         } 
 61     }else 
 62         ; //重复
 63 
 64     p->height = max(GetHeight(p->left), GetHeight(p->right)) + 1;
 65 }
 66 
 67 
 68 template<typename T>
 69 void AVLTree<T>::Remove(AVLTreeNode* &p, const T& k) {
 70     if (p == NULL) return;
 71     if (p->key > k) {
 72         Remove(p->left, k);
 73         if (GetHeight(p->right) - GetHeight(p->left) == 2) {
 74             if (p->right->right != NULL) {
 75                 RotateWithRightChild(p);
 76             } else {
 77                 DoubleWithRightChild(p);
 78             }
 79         } 
 80     }else if (p->key < k) {
 81         Remove(p->right, k);
 82         if (GetHeight(p->left) - GetHeight(p->right) == 2) {
 83             if (p->left->left != NULL) {
 84                 RotateWithLeftChild(p);
 85             } else {
 86                 DoubleWithLeftChild(p);
 87             }
 88         }
 89     } else if (p->left != NULL && p->right != NULL) {
 90         p->key = FindMin(p->right)->key; //用右子树最小节点键值代替要删除节点的键值,与二叉搜索树类似
 91         Remove(p->right, p->key);
 92         if (GetHeight(p->left) - GetHeight(p->right) == 2) {
 93             if (p->left->left != NULL) {
 94                 RotateWithLeftChild(p);
 95             } else {
 96                 DoubleWithLeftChild(p);
 97             }
 98         }
 99     } else {
100         AVLTreeNode* temp = p;
101         p = p->left ? p->left : p->right;
102         delete temp; 
103     }
104 
105     if (p != NULL) {
106         p->height = max(GetHeight(p->left), GetHeight(p->right)) + 1;
107     }
108 }
109 
110 
111 template<typename T>
112 typename AVLTree<T>::AVLTreeNode* AVLTree<T>::FindMin(AVLTreeNode* p) const {
113     AVLTreeNode* t = p;
114     while (t != NULL && t->left != NULL) {
115         t = t->left;
116     }
117 
118     return t;
119 }

 

  

  参考文献:1.《数据结构与算法分析C++描述》(第三版)——Mark Allen Weiss, 人民邮电出版社  

       2. http://blog.csdn.net/pyang1989/article/details/22697121

转载于:https://www.cnblogs.com/vincently/p/4225976.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/432063.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

法线和法线贴图

法线和法线贴图 1、法线无处不在&#xff0c;这是图形学基础中的基础。 2、法线贴图&#xff0c;凹凸图&#xff0c;位移图等等&#xff0c;在图形学历史上有着比较重要的位置&#xff0c;在很多图形学的架构中都有应用&#xff0c;典型的例如延迟渲染架构。 法线 法线&…

Unicode、UTF-8、UTF-16

计算机起源于美国&#xff0c;上个世纪&#xff0c;他们对英语字符与二进制位之间的关系做了统一规定&#xff0c;并制定了一套字符编码规则&#xff0c;这套编码规则被称为ASCII编码 ASCII 编码一共定义了128个字符的编码规则&#xff0c;用七位二进制表示 ( 0x00 - 0x7F ), …

IE11 全新的F12开发者工具

我讨厌debug&#xff0c;相信也没多少开发者会喜欢。但是当代码出错之后肯定是要找出问题出在哪里的。不过网页开发的时候遇到 BUG 是一件再正常不过的事情了&#xff0c;我们不能保证自己的代码万无一失&#xff0c;于是使用浏览器的开发者工具调试是我们解决问题最快捷的方法…

OpenXLSX 中文字段读取问题

在读取excel的时候发现有些中文字段无法读取&#xff0c;通过把excel文件解压后对比发现&#xff0c;正常读取和不 能正常读取的中文字段在sharedString.xml中存储的格式有差异&#xff0c;取其中一个字段&#xff0c;如下图&#xff1a; 正常读取的 不能读取的 对比可以看到…

[翻译] ZLHistogramAudioPlot

ZLHistogramAudioPlot A hardware-accelerated audio visualization view using EZAudio, inspired by AudioCopy. ZLHistogramAudioPlot was originally developed for Murmur. 这是使用了EZAudio,一个硬件加速的audio可视化view,灵感来自于AudioCopy.ZLHistogramAudioPlot这个…

一. NSIS介绍

概述 最近需要写一个安装程序&#xff0c;比对了一下现有的安装工具&#xff0c;最后选定了NSIS&#xff0c;最主要的原因一是开源、二是灵活。 下面把我的要求简单列举下&#xff1a; 1、需要检查系统环境是否满足要求 2、需要界面友好的安装过程 3、需要一些自定义界面&…

HDU-1008

水题 Description The highest building in our city has only one elevator. A request list is made up with N positive numbers. The numbers denote at which floors the elevator will stop, in specified order. It costs 6 seconds to move the elevator up one floor,…

二. 简单的NSIS安装包

新建脚本&#xff1a;向导 我们先从一个简单的NSIS安装包开始吧&#xff0c;就像前面&#xff08;NSIS介绍&#xff09;所说&#xff0c;我们虽然看过用户手册&#xff0c;可要写安装脚本无从下手&#xff0c;那我们的编辑工具HM NIS Edit就派上用场了。 打开HM NIS Edit&…

30 个很棒的 PHP 开源 CMS 内容管理系统

本文汇集了30个优秀的开源CMS建站系统&#xff0c;采用PHP开发。以下列表不分先后顺序。 1. AdaptCMS AdaptCMS Lite 是一个开源的CMS系统&#xff0c;主要特点是易用&#xff0c;而且可以轻松和其他系统接驳&#xff0c;提供简单的扩展定制途径&#xff0c;一个简单而且功能强…

Alwayson常用脚本

1、修改实例下所有节点的数据同步模式&#xff0c;在master数据库下运行 --查找所有异步提交的辅助节点&#xff0c;修改为同步提交模式 -- SYNCHRONOUS_COMMIT 同步提交模式 -- ASYNCHRONOUS_COMMIT 异步提交模式 select ALTER AVAILABILITY GROUP [a.name] MODIFY REPLICA…

Package ‘*****‘ has no installation candidate

如果在apt源中未找到软件&#xff0c;去ubuntu的软件包搜索页面中去搜索该软件 Ubuntu – Ubuntu Packages Search https://packages.ubuntu.com/ 前面红字找到对应ubuntu版本的软件版本名称&#xff0c;后面中括号为仓库名称&#xff0c;然后写入到/etc/apt/sources.list中 …

Oracle 客户端连接服务器[转]

很多朋友在开发项目中并不是每个人用一个数据库&#xff0c;而是有单独的一台主机作为开发的数据库服务器&#xff0c;这样&#xff0c;就需要我们的开发人员去连接它。 首先是进入oracle的 Net Mananger&#xff1b; 接下来就是进行简单的设置了。。 &am…

p3d gauge 尺寸问题

1. 在panel.cfg中&#xff0c;每个window可以有多个gauge&#xff0c;window是gauge的容器 2. 在panel.cfg中&#xff0c;background_color为window背景色&#xff0c;如果设置为0&#xff0c;0&#xff0c;0&#xff0c;未被gauge覆盖的 部分会透明 3. 在panel.cfg中&#…

BZOJ2199 [Usaco2011 Jan]奶牛议会

首先建立一个2-SAT的裸模型&#xff0c;然后发现。。。tarjan没法判断?的情况 于是暴力对每一个议案check一下&#xff0c;直接dfs即可 1 /**************************************************************2 Problem: 21993 User: rausen4 Language: C5 Resu…

从此明白了卷积神经网络(CNN)

卷积神经网络是一种曾经让我无论如何也无法弄明白的东西&#xff0c;主要是名字就太“高级”了&#xff0c;网上的各种各样的文章来介绍“什么是卷积”尤为让人受不了。听了吴恩达的网课之后&#xff0c;豁然开朗&#xff0c;终于搞明白了这个东西是什么和为什么。我这里大概会…

Logistic Regression:最基础的神经网络

一、什么是logictic regression 下面的图是Andrew Ng提供的一个用logistic regression来识别主子的图片的算法结构示意图&#xff1a; 「左边」的「x0到x12287「是输入&#xff08;input&#xff09;&#xff0c;我们称之为」特征&#xff08;feather&#xff09;」&#xff0…