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

计算机网站建设策划书郑州网站优化seo

计算机网站建设策划书,郑州网站优化seo,app网站建设费用,全国网站建设人员数量目录 第一节:AVL树的特征 第二节:实现思路 2-1.插入 2-1-1.右单旋 2-1-2.左单旋 2-1-3.左右双旋 2-1-4.右左双旋 2-1-5.总结 2-2.删除 第三节:代码实现 3-1.Node类 3-2.AVLTree类 3-2-1.Insert函数 3-2-2.Height函数 3-2-3.Balance函数 3-…

目录

第一节:AVL树的特征

第二节:实现思路

        2-1.插入

                2-1-1.右单旋

                2-1-2.左单旋

                2-1-3.左右双旋

                2-1-4.右左双旋

                2-1-5.总结

        2-2.删除

第三节:代码实现

        3-1.Node类

        3-2.AVLTree类

                3-2-1.Insert函数

                3-2-2.Height函数

                3-2-3.Balance函数

                3-2-4.其他函数

                 3-2-5.clear函数

第四节:测试

第五节:优化方案

gitee:AVL树 · 转调/C++ - 码云 - 开源中国 

第六章:总结

下期预告:


第一节:AVL树的特征

        如果一个搜索二叉树的结构如下:

                                

        那么当我想找a的时候,时间复杂度就从 log₂n 退化成 n 了。

        究其原因,搜索二叉树没有一个机制让树的整体高度变得更低。

        所以就出现了AVL树,AVL树通过保证每个节点的左右子树高度差不超过[-1,1],保证整棵树的平衡。

        将左右子树高度差抽象成平衡因子,平衡因子=右子树高度-左子树高度,每个节点都要维护自己的平衡因子不超过1。

第二节:实现思路

        2-1.插入

        1.首先按照搜索二叉树的规则进行插入

        2.插入后更新其父亲节点的平衡因子

                a.插入父亲节点的左子树,父亲节点平衡因子--

                b.插入父亲节点的右子树,父亲节点平衡因子++

        3.如果父亲节点平衡因子是-1或者1,说明父亲所在子树的高度改变了,继续更新父亲的父亲节点的平衡因子,若父亲的父亲节点的平衡因子也为-1或者1,继续向上更新,直到平衡因子不为-1或者1;

        如果某个祖先节点的平衡因子更新后为0,说明祖先节点所在子树高度不变了,对祖先之上的节点也没有影响了,不再往上更新;

        如果某个祖先节点的平衡因子为-2或者2,就需要进行旋转处理了:

                2-1-1.右单旋

        当前节点平衡因子为-2 && 其左孩子的平衡因子为-1 进行右单旋

        用长方形表示子树高度,b、c的高度都为h,a的高度为h+1,红色方块为节点新增位置,蓝色字体为节点的平衡因子:

                                        

        60的平衡因子为-2,并且其左孩子30的平衡因子为-1,进行右单旋

        具体的方法是将60的左孩子变成30的右孩子,30的右孩子被"抢走了",就把60变成30的右孩子,最后把60的父亲变成30的父亲。

        这样做之后30和60的平衡因子都变成了0,就不需要再向上更新了。

        可以发现30向右旋转把60顶替了,60向右旋转屈居30,a、b、c从左向右的顺序依然不变。

                2-1-2.左单旋

        当前节点平衡因子为+2 && 其右孩子的平衡因子为+1

                                                

        30的平衡因子为+2,并且其右孩子60的平衡因子为+1,进行左单旋

        

        这样做之后30和60的平衡因子都变成了0,就不需要再向上更新了。

        可以发现60向左旋转把30顶替了,30向左旋转屈居30,a、b、c从左向右的顺序依然不变。

                2-1-3.左右双旋

        当前节点平衡因子为-2 && 其左孩子的平衡因子为+1时进行左右双旋。

        左右双旋有三种不同的情况,它们的旋转逻辑是一样的,但是最后的平衡因子不同。

        旋转逻辑:

        先不看它们的平衡因子,只看节点和子树的交换关系,此时a=h,d=h,b和c有一个等于h-1,另一个等于h,才能保证90的平衡因子为-2,30的平衡因子为1。

            其实就是孙子变成了它爸爸和爷爷共同的父亲,然后仍然保持a、b、c、d的左右顺序 

        (1)如果60自己就是新增的节点

        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​    

        

        3个节点的平衡因子都为0,不再向上更新。

        为了便于理解,我使用绿色字母表示新增节点后a、b、c、d的高度。

        (2)如果60的左子树新增节点

        此时60的平衡因子为0,不再向上更新,30的平衡因子为0,90的平衡因子为1

        (3)如果60的右子树新增节点

        此时60的平衡因子为0,不再向上更新,30的平衡因子为-1,90的平衡因子为0

                2-1-4.右左双旋

        当前节点平衡因子为+2 && 其右孩子的平衡因子为-1时进行左右双旋。

        左右双旋也有三种不同的情况,它们的旋转逻辑是一样的,但是最后的平衡因子不同。

         旋转逻辑:

        

        也是孙子变成了它爸爸和爷爷共同的父亲,然后仍然保持a、b、c、d的左右顺序 ,这和左右双旋的结果一致。 

         所以如果是60的左子树b新增节点,旋转后,30、60、90的平衡因子分别是0、0、1;

        如果是60的右子树c新增节点,旋转后,30、60、90的平衡因子分别是-1、0、0;

        如果60本身是新增的节点,旋转后,30、60、90的平衡因子都是0。

                2-1-5.总结

        只要进行旋转后,"辈分"最高的节点的平衡因子都变成了0,说明只要进行过旋转,就不需要再往上更新了。

第三节:代码实现

        将以上思路整理成代码。

        3-1.Node类

        首先创建一个节点类:

        它除了节点的连接和保存的值之外,还需要一个变量_bf来保存平衡因子。

template<class T>
class Node
{
public:Node<T>* _left = nullptr;Node<T>* _right = nullptr;Node<T>* _parent = nullptr;T _val;short _bf = 0;
};

        3-2.AVLTree类

        它是AVL树的具体实现。

class AVLTree
{
private:Node<T>* _root = nullptr; // 保存根节点
};

                3-2-1.Insert函数

        首先完成插入函数,它传入一个值,然后按照搜索二叉树的思路插入节点:

		void Insert(const T& val){// 如果根节点为空,赋值根节点即可if (_root == nullptr){_root = new Node<T>;_root->_val = val;return;}Node<T>* cur = _root;Node<T>* parent = nullptr;while (cur){if (cur->_val < val){parent = cur;cur = cur->_right;}else if (cur->_val > val){parent = cur;cur = cur->_left;}}cur = new Node<T>;cur->_val = val;if (parent->_val < val){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;// 更新祖先节点的平衡因子while (parent){if (cur == parent->_left){parent->_bf--;}else{parent->_bf++;}// 继续更新if (abs(parent->_bf) == 1){cur = parent;parent = parent->_parent;continue;}// 不再更新if (parent->_bf == 0){break;}// 如果失衡,进行旋转if (abs(parent->_bf) > 1){Rotate(parent);break;}}}

                3-2-2.Height函数

        它获取某个节点的高度,获得某个节点左右孩子的高度后,就可以计算_bf:

		size_t _Height(Node<T>* root){if (root == nullptr) return 0;return std::max(_Height(root->_left), _Height(root->_right)) + 1;}size_t Height(Node<T>* root){return _Height(root);}

                3-2-3.Balance函数

        AVL树的核心函数,它用来更新节点的平衡因子:

void Rotate(Node<T>* cur)
{// 进行旋转处理,旋转完也不需要继续更新了if (cur->_bf == -2 && cur->_left->_bf == -1) // 右单旋{Node<T>* P = cur;      // 父亲Node<T>* S = P->_left; // 儿子P->_left = S->_right;if (S->_right)S->_right->_parent = P;S->_right = P;if (P != _root){if (P == P->_parent->_left){P->_parent->_left = S;}else if (P == P->_parent->_right){P->_parent->_right = S;}}else{_root = S;}S->_parent = P->_parent;P->_parent = S;P->_bf = 0;S->_bf = 0;}else if (cur->_bf == 2 && cur->_right->_bf == 1) // 左单旋{Node<T>* P = cur;Node<T>* S = P->_right;P->_right = S->_left;if (S->_left)S->_left->_parent = P;S->_left = P;if (P != _root){if (P == P->_parent->_left){P->_parent->_left = S;}else if (P == P->_parent->_right){P->_parent->_right = S;}}else{_root = S;}S->_parent = P->_parent;P->_parent = S;P->_bf = 0;S->_bf = 0;}else if (cur->_bf == -2 && cur->_left->_bf == 1) // 左右双旋{Node<T>* P = cur;      // 父亲Node<T>* S = P->_left; // 儿子Node<T>* G = S->_right;// 孙子P->_left = G->_right;if (G->_right)G->_right->_parent = P;G->_right = P;S->_right = G->_left;if (G->_left)G->_left->_parent = S;G->_left = S;if (P != _root){if (P == P->_parent->_left){P->_parent->_left = G;}else if (P == P->_parent->_right){P->_parent->_right = G;}}else{_root = G;}G->_parent = P->_parent;P->_parent = G;S->_parent = G;if (G->_bf == 1){S->_bf = -1;P->_bf = 0;}else if (G->_bf == -1){S->_bf = 0;P->_bf = 1;}else if (G->_bf == 0){S->_bf = P->_bf = 0;}G->_bf = 0;}else if (cur->_bf == 2 && cur->_right->_bf == -1) // 右左双旋{Node<T>* P = cur;Node<T>* S = P->_right;Node<T>* G = S->_left;P->_right = G->_left;if (G->_left)G->_left->_parent = P;G->_left = P;S->_left = G->_right;if (G->_right)G->_right->_parent = S;G->_right = S;if (P != _root){if (P == P->_parent->_left){P->_parent->_left = G;}else if (P == P->_parent->_right){P->_parent->_right = G;}}else{_root = G;}G->_parent = P->_parent;P->_parent = G;S->_parent = G;if (G->_bf == 1){S->_bf = 0;P->_bf = -1;}else if (G->_bf == -1){S->_bf = 1;P->_bf = 0;}else if (G->_bf == 0){S->_bf = P->_bf = 0;}G->_bf = 0;}
}

                3-2-4.其他函数

        Print 和 IsBalance 用来按升序打印内容和判断每个节点是否都平衡:

	bool IsBalance(){return _IsBalance(_root);}void Print(){_Print(_root);}bool _IsBalance(Node<T>* root){if (root == nullptr) return true;if (abs(root->_bf) > 1) return false;return _IsBalance(root->_left) && _IsBalance(root->_right);}void _Print(Node<T>* root){if (root == nullptr) return;_Print(root->_left);std::cout << root->_val << " ";_Print(root->_right);}

                 3-2-5.clear函数

        因为节点都是new出来的,所以使用清理函数释放空间,析构函数也要调用这个函数:

		void _clear(Node<T>* root){if (root == nullptr) return;_clear(root->_left);_clear(root->_right);delete root;}void clear(){_clear(_root);}~AVLTree(){clear();}

第四节:测试

        插入多个随机数,然后调用 Print 和 IsBalance。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>#include "AVLTree.hpp"int main()
{zd::AVLTree<int> tree;srand(time(NULL));int i = 100;while (i--){int x = rand();tree.Insert(x);}tree.Print();if (tree.IsBalance()){printf("true\n");}else{printf("false\n");}return 0;
}

        观察打印结果和最后是否为true即可。

第五节:优化方案

        实际上左右双旋和右左双旋就是左单旋和右单旋的组合,所以可以将左单旋、右单旋封装成函数,供左右单旋和右左单旋调用。

        但是封装的时候G的_bf需要提前保存,因为调用两次单旋之后,G的_bf就改变了:

		void RotateR(Node<T>* cur) // 右单旋{Node<T>* P = cur;Node<T>* S = P->_left;P->_left = S->_right;if (S->_right)S->_right->_parent = P;S->_right = P;if (P != _root){if (P == P->_parent->_left){P->_parent->_left = S;}else if (P == P->_parent->_right){P->_parent->_right = S;}}else{_root = S;}S->_parent = P->_parent;P->_parent = S;P->_bf = 0;S->_bf = 0;}void RotateL(Node<T>* cur) // 左单旋{Node<T>* P = cur;Node<T>* S = P->_right;P->_right = S->_left;if (S->_left)S->_left->_parent = P;S->_left = P;if (P != _root){if (P == P->_parent->_left){P->_parent->_left = S;}else if (P == P->_parent->_right){P->_parent->_right = S;}}else{_root = S;}S->_parent = P->_parent;P->_parent = S;P->_bf = 0;S->_bf = 0;}void RotateLR(Node<T>* cur) // 左右双旋{Node<T>* P = cur;Node<T>* S = P->_left;Node<T>* G = S->_right;short bf = G->_bf;RotateL(S);RotateR(P);if (bf == 1){S->_bf = -1;P->_bf = 0;}else if (bf == -1){S->_bf = 0;P->_bf = 1;}else if (bf == 0){S->_bf = P->_bf = 0;}G->_bf = 0;}void RotateRL(Node<T>* cur) // 右左双旋{Node<T>* P = cur;Node<T>* S = P->_right;Node<T>* G = S->_left;short bf = G->_bf;RotateR(S);RotateL(P);if (bf == 1){S->_bf = 0;P->_bf = -1;}else if (bf == -1){S->_bf = 1;P->_bf = 0;}else if (bf == 0){S->_bf = P->_bf = 0;}G->_bf = 0;}

        用以上接口替换Rotate中的对应代码即可。

Gitee:AVL树 · 转调/C++ - 码云 - 开源中国 

第六章:总结

        单旋时,向左会被旋转到向右,向右会被旋转到向左,但是a、b、c的顺序不变

        双旋时,两种双旋的结果一样,a、b、c、d的顺序也不变,而且双旋可以拆分成两次单旋:S先旋、P后旋。 

        负二左负右单旋,反之正一左右旋;正二右正左单旋,反之负一右左旋;两个双旋子先动;a、b、c、d顺序齐。 

下期预告:

        第十三章将介绍另外一种搜索二叉树——红黑树。

http://www.dtcms.com/wzjs/482517.html

相关文章:

  • 大连华南网站建设建立网站用什么软件
  • vue做的个人网站渠道推广费用咨询
  • 做网站建设分哪些类型网络安全培训机构排名
  • 成都微信网站建设推广长春做网络优化的公司
  • 服务好的高端网站建设佣金高的推广平台
  • 小程序营销策划方案seo关键词排名优化方法
  • 建设生鲜网站价格企业门户网站
  • 宁波seo软件提升seo排名的方法
  • 浙江大学陈越做的刷题网站好口碑关键词优化地址
  • 厦门网站专业建设seo优化技术厂家
  • 外贸网站支付系统中国站长
  • c2c电子商务网站建设推广团队在哪里找
  • 贺州网站推广口碑营销案例ppt
  • 做动态网站需要什么软件今日新闻最新头条10条摘抄
  • 外贸谷歌网站推广淘宝推广工具
  • 淘客的手机网站怎么做网站排名优化公司
  • 网站备案主体 被拉黑网络平台宣传方式有哪些
  • 西安做网站哪里价格低专业的网络推广
  • 订制电子商务网站 价格营销型外贸网站建设
  • 无锡网站优化公司抖音seo代理
  • 小米wordpress广州seo推广优化
  • b站视频推广费用一般多少线上宣传渠道和宣传方式
  • 网站后台发布了但看不见制作一个网站的基本步骤
  • 网站备案号 如何添加seo搜索优化是什么意思
  • 建设视频网站费用长沙网站推广和优化
  • 软件开发文档的作用网站排名seo培训
  • 淮南做网站营销培训课程视频
  • 什么是seo网站优化重庆森林经典台词截图
  • 网站开发项目团队人员品牌营销的概念
  • 智慧养老网站开发seo排名规则