当前位置: 首页 > news >正文

遵义网站开发制作公司网络营销推广的优势

遵义网站开发制作公司,网络营销推广的优势,开发小程序定制公司,比较靠谱互联网推广公司目录 模拟插入节点 左单旋 右单旋 右左双旋 左右双旋 总结 实现 插入实现 左单旋实现 右单旋实现 右左双旋实现 左右双旋实现 AVL树 模拟实现(插入) AVL 树,是高度平衡二叉搜索树,其主要通过旋转来控制其左右子树的高…

目录

模拟插入节点

左单旋

右单旋

右左双旋

左右双旋

总结

实现

插入实现

 左单旋实现

右单旋实现

右左双旋实现

左右双旋实现


AVL树 模拟实现(插入)

AVL 树,是高度平衡二叉搜索树,其主要通过旋转来控制其左右子树的高度不超过1,这样就能达到搜索效率基本等同于满二叉树(O(LogN)),所以 AVL 并不会向普通的搜索树一样,在极端情况下退化为单枝。

首先AVL在平衡的前提下,还要保证其是一颗搜索树,所以在插入的时候还是按照搜索树的插入规则来。

AVL 树的高度就是右子树减左子树的高度。

AVL 树的实现有几种,其中一种是借用平衡因子来查看其是否平衡,如果不适用平衡因子,那么就需要使用高度查看是否平衡,但是加入平衡因子会跟简单一些,所以下面我们实现的AVL 树是借用平衡因子来维持平衡的。

模拟插入节点

如果是第一次插入,以及第二次插入那么和搜索树是一摸一样的,为什么呢?

因为如果是第一次插入,那么就是插入到根节点,所以该是是平衡的,无需做其他的操作来保持其平衡,而第二次插入也是不需要我们做其他操作的,同样是因为第二次插入也是平衡的(左右子树的高度不超过1)。

左单旋

插入两个节点如图所示:

这时候,6 节点的高度就为 0,而 2 节点的高度为 1,因为 6 节点的左右子树的高度都为 0,而 2 节点的右子树的高度为 1,而左子树的高度为 0.

如果是两个节点的话,是无法达到完全平衡的,所以并不是AVL树不想达到完全平衡,而是只有在满二叉树的情况下才能达到完全平衡。

下面如果在插入一个节点,那么就会引发旋转:

如果这时候插入节点 8,那么首先我们就能看到这棵树已经不平衡了,但是我们要怎么使用平衡因子来控制?

插入节点 8,此时节点 8 的平衡因子肯定是 0,那么现在 8 在节点 6 的右边,所以 6 要对它的平衡因子进行+1操作:

此时节点 6 的平衡因子变为了 1,现在我们想一下,如果平衡因子由 0 变为 1表示的是什么?

节点刚开始的平衡因子是 0  ,说明刚开始的时候该树是平衡的,变为 1说明该树的右子树多增加了一个节点的高度,所以说明该树的高度发生了变化,所以既然该节点的高度发生了变化,那么该节点的父亲节点的高度可能也会发生变化,所以这时候我们需要向上检查父亲节点的高度:

这时候父亲节点的高度变为了 2,说明此时已经不平衡了,那么要怎样旋转?

我们发现这样的不平衡是单纯的右边高,所以我们尽可能向左旋转,将高度压下去,也就是使用左单旋:

1. 我们需要将父亲节点(2),的右子树连接到,cur(6) 节点的左子树上 

2. 我们将cur(6)节点的左子树,连接成父亲节点(2)

所以旋转结束后就是这样:

但是我们的平衡因子是不正确的,所以如果我们使用左单旋转,旋转之后我们需要将父亲节点(2),和cur节点(6)的平衡因子变为 0.

上面我们为什么要将cur 节点的左孩子给父亲节点呢?

因为这里我们只是画了一个节点,我们有可能右多个节点,所以我们还需要处理好其他节点,但是由于我们只是对cur 的左子树进行的调换位置,我们并未使其高度发生变化,所以我们也自然不需要对其的平衡因子进行调整:

也就是像我们上图这样,cur节点,还有左子树:

上图就是进行旋转之后的样子,然后进行修正平衡因子(parent 为 0,cur 为 0:

右单旋

如果我们的方向不同呢?也就是整个树是单纯的左边高:

插入节点:

 修正平衡因子:

这里我们发现节点 6 已经不平衡了,需要旋转来维持平衡,这时候我们发现它是单纯的左边高,也就是我们需要像右旋转,也就是右单旋:

1. 将节点 4 的右子树连接到节点 6 的左子树上

2. 将节点 6 连接到节点 4 的右子树上

旋转结束后就就是这样:

 我们还是将parent节点(6)和 cur节点(4)的平衡因子修改为 0.

而这里处理cur节点的右子树的原因和处理左单旋时候的 cur 的左子树是一样的原因。

经过上面的左单旋和右单旋,我们发现:

1. 如果插入节点到当前节点的左子树:那么就让当前节点的平衡因子进行 ‘减减’ 操作

2. 如果插入节点到当前节点的右子树:那么就让当前节点的平衡因子进行 ‘加加’ 操作

3.如果平衡因子由 0 变为 1 或者由 0 变为 -1 ,那么就说明当前节点的左右子树的高度发生的变化,需要对当前节点的父亲节点进行平衡因子的跟新。

4.如果当前节点的平衡因子被跟新为 2 或者 -2 ,那么说明当前节点的左右子树已经不平衡了,需要旋转来调节平衡。

5. 什么时候插入结束?

1)当插入的节点是根节点,那么插入后就可以结束了。

2)插入的节点已经存在

3)插入成功并且修正平衡因子后,父亲的平衡因子变为 0 ,变为 0 说明父亲之前的平衡因子不为        0,而变为 0 说明父亲的高度没有发生变化,所以无需在向上调整,插入也就结束。

4)修正平衡因子后,发现某一节点需要旋转,而旋转并且修正平衡因子后,插入结束。

上面说的修正平衡因子是一个循环的过程,因为在修正平衡因子的时候,可能将父亲的平衡因子由0 变为 1 或者 -1 ,说明父亲的高度变化了,所以此时就需要将父亲给给 cur 然后继续向上调整,还有一个插入结束的调节,就是父亲为空也插入结束了,父亲为空说明为根节点。

6. 判断是左单旋还是右单旋:

1)如果是单纯的左边高,那么就进行右单旋,那么怎么判断是单纯的左边高?单纯的左边高,就是父亲节点的平衡因子为 -2 ,而 cur 节点的平衡因子为 -1.

2)如果是单纯的右边高,那么就进行右单旋,右单旋的特征是,parent 的平衡因子是 2, cur 的平衡因子是 1

右左双旋

上面我们插入后总是一边高,但是我们还是右其他的情况,也就是下面这种:

我们这种该怎么样旋转呢?我们可以先试一下左单旋,因为这里我们发现是右边高:

在旋转后,我们发现高度并没有被压缩,而是调了个个,所以如果是这种情况的话,单纯的左或者右是不能完成任务的,而是需要使用双旋。

我们可以先对cur 节点进行右单旋:

 经过我们前面对 cur 节点进行的右单旋,我们此时已经变为一边高了,所以此时我们在对 parent 节点进行左单旋就可以完成压缩高度任务:

我们此时经过旋转后的 parent 和 cur 节点的平衡因子变为 0了:

但是这只是我们节点 4 就是新插入的节点,那么假设是其他情况呢?

 上图是抽象图,可以表示该种情况下所有可能的情况,如果当 h == 0 的时候,就表示的是 4 节点是新增,但是无论 h 是多少,我们都可以使用右左双旋。

首先对该树的cur 节点使用右单旋:

旋转后就变为了这样,也就是单纯的右边高,所以我们在使用左单旋对parent节点:

但是这时候 parent 和 cur 节点的平衡因子都是 0,那么我们要怎么调节平衡因子呢?对于这种情况来说,我们是将parent 的平衡因子调整为 -1 ,cur 的平衡因子为 0,即可,但是我们要是插入的位置不是刚开始插入的位置呢?

如果插入到该位置,那么金国旋转后的结果是这样的:

所以此时跟新平衡因子应该是 parent 为 0 ,cur 的平衡因子应该为 1,所以我们插入位置不同的话,我们的平衡因子的跟新策略是不同的,所以我们还要记录插入位置的平衡因子。

我们为了更好的描述,我们将节点 4 称为 curLeft。

我们发现我们在旋转结束后,curLeft 的左右子树被parent 和 cur 代替,而他自己的左子树被分给了parent 的 right 子树 ,而它的右子树被分给了cur 的 left 子树,所以我们只需要知道 curRight 的平衡因子,我们也就可以将parent 和 cur 的平衡因子跟新正确。

上面我们一直都没有说 curLeft 的平衡因子,其实curLeft 的平衡因子在第一次右单旋的时候就被跟新为了 0 ,而旋转结束后,curLeft 的平衡因子也就应该是 0.

左右双旋

这里我们就直接使用抽象图来描述了。

以上图为例,我们要插入一个值:

现在我们发现是左边高,但是我们知道这种情况下,我们单纯的使用右单旋是解决不了问题的,所以我们还是选哟使用双旋,也就是左右双旋,我们先对cur 节点进行左单旋,然后对parent 进行右单旋:

 旋转后,我们发现现在是单纯的左边高,所以我们对 parent 节点使用右单旋:

这时候的 parent cur 以及 curRight  的平衡因子都被跟新为 0了 ,但是都是 0 并不正确,而是也像我们前面的右左双一样,需要看 curRight 的平衡因子:

1. 如果插入到 curRight 的keft,那么 cur 的平衡因子就是 0 ,parent 的平衡因子为 1。

2. 如果插入到 curRight 的 right ,那么 cur 的平衡因子就是 -1,parent 的平衡因子就是 0。

总结

  • 如果 parent 的平衡因子为 2 ,cur 的平衡因子为 1,那么使用的是左单旋,将 parent 和 cur 的平衡因子都跟新为 0.
  • 如果 parent 的平衡因子为 -2 ,cur 的平衡因子为 -1,那么使用的是右单,将 parent 和 cur 的平衡因子都跟新为 0.。
  • 如果 parent 的平衡因子为 2 ,cur 的平衡因子为 -1,那么使用的是右左双旋                    平衡因子:                                                                                                                      如果curLeft 的平衡因子为 1 ,那么就将parent 的平衡因子置为 -1,cur 的平衡因子置为 0,curLeft 的平衡因子为 0                                                                                              如果curLeft 的平衡因子为 -1 ,那么就将parent 的平衡因子置为 0,cur 的平衡因子置为 1,curLeft 的平衡因子为 0                                                                                              如果curLeft 的平衡因子为 0 ,那么就将parent 的平衡因子置为 0,cur 的平衡因子置为 0,curLeft 的平衡因子为 0 
  • 如果 parent 的平衡因子为 -2 ,cur 的平衡因子为 1,那么使用的是左右双旋                    平衡因子:                                                                                                                      如果curRight的平衡因子为 1 ,那么就将parent 的平衡因子置为 0,cur 的平衡因子置为 -1,curRight的平衡因子为 0                                                                                           如果curRight的平衡因子为 -1 ,那么就将parent 的平衡因子置为 1,cur 的平衡因子置为 0,curRight的平衡因子为 0                                                                                          如果curRight的平衡因子为 0 ,那么就将parent 的平衡因子置为 0,cur 的平衡因子置为 0,curLeft 的平衡因子为 0        

实现

上面大概率是把思路都说明白了,下面开始看一下实现:

插入实现

首先我们先看一下AVLTreeNode:

对于该节点,我们需要一个存储值的变量,我们还需要三个指针,其中一个 left,还有一个 right,还有一个 parent,这里需要parent 是因为我们需要找找到它的父亲节点,还需要一个平衡因子

template<class K, class V>
struct TreeNode
{pair<K, V> _kv;TreeNode<K, V>* _left;TreeNode<K, V>* _right;TreeNode<K, V>* _parent;int _balance;TreeNode(const pair<K, V>& kv):_kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr),_balance(0){}
};

这里我们将存储值的变量之间设置为了 kv 结构,因为这样既可以适用于 set,也适用于 map.

下面就是插入:

插入的话,先按照搜索树的插入节点,也就是插入插入位置,然后记录其父亲节点:

		if (_root == nullptr){_root = new Node(kv);return true;}Node* cur = _root;Node* parent = nullptr;while (cur){if (kv.first < cur->_kv.first){parent = cur;cur = cur->_left;}else if (kv.first > cur->_kv.first){parent = cur;cur = cur->_right;}else{// 重复了,插入失败return false;}}// 插入数据cur = new Node(kv);if (kv.first < parent->_kv.first){//维护三叉链parent->_left = cur;cur->_parent = parent;}else{//维护三叉链parent->_right = cur;cur->_parent = parent;}

等 cur 节点插入后,开始修正并检查平衡:

		//检查平衡while (parent){//维护平衡因子// 平衡因子=右子树高度-左子树高度if (cur == parent->_left){parent->_balance--;}else{parent->_balance++;}// 检查 parent 的平衡因子// 如果为 0 说明parent 这颗树以及平衡,无需检查// 如果为 1/-1 说明 parent 的平衡发生了变化,说明需要检查 parent 的parent 的平衡因子// 如果为 2/-2 说明这棵树已经不平衡了,需要旋转if (parent->_balance == 0){//该树已经平衡break;}else if (parent->_balance == 1 || parent->_balance == -1){// 说明该树的高度发生变化,需要检查 parent 的 parent 的平衡因子,所以需要向上传递cur = parent;parent = parent->_parent;}else if (parent->_balance == 2 || parent->_balance == -2){// 说明该树需要旋转保持平衡if (parent->_balance == 2 && cur->_balance == 1){// 左单旋RotateL(parent);break;}else if (parent->_balance == -2 && cur->_balance == -1){// 右单旋RotateR(parent);break;}else if (parent->_balance == 2 && cur->_balance == -1){// 右左双旋RotateRL(parent);break;}else if (parent->_balance == -2 && cur->_balance == 1){// 左右双旋RotateLR(parent);break;}else{assert(0);}}else{// 不能出现该种情况assert(0);}}

上面就是修正并且检查平衡,那么我们看一下应该怎么样旋转:

 左单旋实现

实际上,左单旋并不是像我们说的那样,只有我们前面模拟插入的时候的两步

主要步骤:

1. 将 parent 的 right 连接成 cur 的 left

2. 将 cur 的 left 连接成 parent

3. 维护三叉链

实际上我们还需要维护三叉链,也就是他们的父亲节点,。

	// 左单旋void RotateL(Node* parent){Node* cur = parent->_right;Node* curLeft = cur->_left;parent->_right = curLeft;if (curLeft){curLeft->_parent = parent;}cur->_left = parent;Node* pparent = parent->_parent;parent->_parent = cur;if (pparent == nullptr){// parent 就是根节点_root = cur;cur->_parent = nullptr;}else{if (parent == pparent->_left){pparent->_left = cur;}else{pparent->_right = cur;}cur->_parent = pparent;}parent->_balance = 0;cur->_balance = 0;}

右单旋实现

主要步骤:

1. 将 parent 的 left 链接到 cur 的 right

2.将 cur 的 left 链接到 parent

3. 维护其三叉链

	//右单旋void RotateR(Node* parent){Node* cur = parent->_left;Node* curRight = cur->_right;parent->_left = curRight;if (curRight){curRight->_parent = parent;}cur->_right = parent;Node* pparent = parent->_parent;parent->_parent = cur;if (pparent == nullptr){// 说明 parent 是根节点_root = cur;cur->_parent = nullptr;}else{if (parent == pparent->_left){pparent->_left = cur;}else{pparent->_right = cur;}cur->_parent = pparent;}parent->_balance = 0;cur->_balance = 0;}

右左双旋实现

主要步骤:

1. 对 cur 节点进行右旋

2. 对 parent 节点进行左旋

3. 修正平衡因子,这个我们前面总结过了

	void RotateRL(Node* parent){//先对 cur 节点进行右单旋//在对 parent 节点进行左单旋Node* cur = parent->_right;Node* curLeft = cur->_left;int balance = curLeft->_balance;RotateR(cur);RotateL(parent);//旋转后维护平衡因子//这里右几种可能://1. curLeft 节点的平衡因子为 0//2. curLeft 节点的平衡因子为-1//3. curLeft 节点的平衡因子为 1// 这里发现每一次旋转结束后,curRight 节点的左树给了parent,右树给了 cur,// 所以这里需要的以区分新增节点插入到了 curRight 节点的左是右// 记录该平衡因子主要是为了维护旋转后的平衡因子if (balance == 0){// curRight 就是新增节点,所以cur,parent,curRight 的平衡因子都为 0cur->_balance = 0;parent->_balance = 0;curLeft->_balance = 0;}else if (balance == 1){// 新增在了右边,所以cur,parent,curRight 的平衡因子分别为 0,-1,0cur->_balance = 0;parent->_balance = -1;curLeft->_balance = 0;}else if (balance == -1){// 新增在了左边,所以cur,parent,curRight 的平衡因子分别为 1,0,0cur->_balance = 1;parent->_balance = 0;curLeft->_balance = 0;}else{assert(0);}}

左右双旋实现

主要步骤:

1. 对 cur 节点进行左旋

2. 对 parent 节点进行右旋

3. 修正平衡因子,这个我们前面总结过了

	// 左右双旋void RotateLR(Node* parent){Node* cur = parent->_left;Node* curRight = cur->_right;int balance = curRight->_balance;RotateL(cur);RotateR(parent);if (balance == 0){cur->_balance = 0;parent->_balance = 0;curRight->_balance = 0;}else if (balance == -1){cur->_balance = 0;parent->_balance = 1;curRight->_balance = 0;}else if (balance == 1){cur->_balance = -1;parent->_balance = 0;curRight->_balance = 0;}}

http://www.mmbaike.com/news/33274.html

相关文章:

  • 百度怎么验证网站谷歌浏览器最新版本
  • wordpress复制菜单上海排名优化seobwyseo
  • 网站系统维护一般多长时间互联网营销外包推广
  • 长沙网络推广招聘北京网站seo服务
  • 个人网上银行佛山网站seo
  • 如何重建网站seo外包公司排名
  • 国外做网站被动收入百度知道官网入口
  • 绿色做环保网站的好处网络广告营销成功案例
  • 建设小网站教程公司网站设计哪家好
  • 广州哪些做网站的公司中国疾控卫生应急服装
  • 手机网站建设方案书自建网站平台
  • 网站一般用什么语言写官网seo是什么意思
  • 营销型网站制作培训多少钱网络热词2023
  • 个人网站做淘宝客商城浙江搜索引擎优化
  • wamp跟wordpressseo入门基础教程
  • 自己做网站要不要钱长沙做引流推广的公司
  • 江西网站开发方案百度小说搜索排行榜
  • 移动网站优化51link友链
  • 网站网站建设专业行业网站
  • 长宁网站设计百度在线客服人工服务
  • 网页制作模板的网站免费站长工具排名分析
  • 想建设个网站卖东西青岛seo优化
  • 做创意ppt网站网站优化排名的方法
  • 网站上怎么做福彩卖家站长工具域名
  • 北京营业执照网上办理入口如何做seo优化
  • 台州网站快速优化排名易搜搜索引擎
  • 广东网络品牌建站公司北京快速优化排名
  • 一流设计网站电脑培训班价目表
  • 网站群建设规划方案b站视频未能成功转码
  • 做书的网站有哪些友情连接出售