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

宁夏网站建设电话如何建立公司网站网页

宁夏网站建设电话,如何建立公司网站网页,什么网站能免费做推广,html5企业网站带后台一.平衡树的介绍 平衡树是以二叉树结构为基础,同时引入了平衡因子进行了限制,以保证树的结点之间的高度差小于等于1,在插入删除结点时通过旋转的方法保持高度相对平衡,从而提高搜索等效率。 二.代码实现 1.平衡树结点 平衡树结…

一.平衡树的介绍

平衡树是以二叉树结构为基础,同时引入了平衡因子进行了限制,以保证树的结点之间的高度差小于等于1,在插入删除结点时通过旋转的方法保持高度相对平衡,从而提高搜索等效率。

二.代码实现

1.平衡树结点

平衡树结点是以二叉树为基础构建的,因此需要左右结点指针left与right。传入pair一部分为值value另一部分为关键字key,以及平衡因子bf(左子树高度减右子树高度)。

下面是结点代码:

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

 2.平衡树的插入

插入结点时首先要找到合适的位置空间来放置新结点,所以我们需要遍历树。从根结点开始(cur),若值比当前结点小让cur往左边走,若值比当前结点大让cur往右边走,直到cur跑到空时停止。

接着进行判断,根据结点高度差的不同,结点位置不一样进行不同的旋转方式,调整相应的平衡因子,根据parent结点的高度决定是否要继续向上更新。

下面是寻找结点的相应代码:

if (_root == nullptr){_root = new Node(kv);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(kv);if (parent->_kv.first < kv.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;

若树的结点为空那么直接插入根结点,若不为空按照上述的方法遍历到空结点后,需要注意要确定parent的位置。

 下面我们来讨论一下旋转

首先计算平衡因子,若cur在parent的左边则bf--,若cur在parent的右边则bf++,若parent的bf为0时直接跳出结果。

若parent的左右都存在结点,那么就是平衡的,反之则不平衡,若bf等于1或者-1,时继续向上更新bf平衡值,让cur等于parent,parent等于parent的parent。

向上遍历若得到bf等于2或者-2时,说明此时树已经不平衡了,需要进行旋转调整树的结构。

下面我们依次分析左旋,右旋,左右双旋,右左双旋的情况

1.右单旋

当parent的平衡因子为-2,cur的平衡因子为-1时我们需要右单旋

如图p代表parent,c代表cur此时我们需要进行右旋操作d代表插入的结点。以parent为轴向右旋转让cur变成parent。

我们需要得到parent结点,cur结点(SUL),以及cur结点的右结点(SULR)。

让SULR变成parent的左结点SUL的右结点变成parent,其他的不进行改变。

需要注意的是,首先需要判断SULR结点是否存在,若存在就让其变为parent的左结点,若不存在就不进行操作。以及我们需要找到parent结点的parent结点,若parentparent结点为空则则让root结点直接变为SUL即可,若不为空需要判断parent结点是parentparent结点的左还是右结点,然后将其左或右给给SUL。

下面放上代码:

void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;Node* parentParent = parent->_parent;subL->_right = parent;parent->_parent = subL;//parent有可能是整棵树的根,也有可能是局部子树//如果是整棵树的根则修改root//if (parentParent == nullptr){_root = subL;subL->_parent = nullptr;}else{if (parent == parentParent->_left){parentParent->_left = subL;}else{parentParent->_right = subL;}subL->_parent = parentParent;}parent->_bf = subL->_bf = 0;}

2.左单旋 

左单旋与右单旋类似,只是parent结点的左右子树互换了位置。

当parent的bf为2时,cur的bf为1时,需要进行左旋操作。

如图p代表parent ,c代表cur结点,插入的结点为a。以parent为轴向左旋转。

我们需要得到cur结点的左结点(SURL),parent结点以及cur结点(SUR)。让parent的右为SURL,SUR的左为parent。其他的不进行改变。 

与上文相同需要找到parent的parent,若parentparent不存在就将root结点设置为SUR,若存在,则根据parent是parentparent的左或者右结点来分配给SUR结点。

下面是代码:

void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;Node* parentParent = parent->_parent;subR->_left = parent;parent->_parent = subR;if (parentParent == nullptr){_root = subR;subR->_parent = nullptr;}else{if (parent == parentParent->_left){parentParent->_left = subR;}else{parentParent->_right = subR;}subR->_parent = parentParent;}parent->_bf = subR->_bf = 0;}

3.左右双旋 

当parent的bf等于-2,cur的bf等于1时,进行左右双旋。先对cur进行左旋让其bf等于-1,此时就回到了第一种情况,再对parent进行一次右旋就完成了操作。

当新增结点cur结点与parent结点形成一个角度时,需要进行双旋操作,如果是开口向右则进行左右双旋,若开口向左则进行右左双旋。

此处的难点在于对插入结点bf的分类讨论。

第一种情况 当SULR(R)的bf为0时

 此时我们需要先对SUL进行左旋操作,使其满足三个点连成一条直线,之后再对parent进行右旋操作,得到下图。

我们可以根据最终结果直接写出代码,先对SUL进行左旋再对parent进行右旋,最后设置三个结点的bf都为0即可。

第二种情况 当SULR(R) 的bf为-1时

当我们进行左右双旋操作后

变成如图,此时需要将parent的bf设置为1,SUL和SULR设置为0。

第三种情况 当SURL(R)的bf为1时

进行左右双旋操作之后

 将SUL的bf设置为-1,parent与SULR的bf设置为0即可。

下面附上代码:

	void RotateLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;RotateL(parent->_left);RotateR(parent);if (bf == 0){subL->_bf = 0;subLR->_bf = 0;parent->_bf = 0;}else if (bf == 1){subL->_bf = -1;parent->_bf = 0;subLR->_bf = 0;}else if (bf == -1){subL->_bf = 0;subLR->_bf = 0;parent->_bf = 1;}else{assert(false);}}

4.右左双旋

右左双旋本质与左右双旋一样,只是左右子树位置互换

所以我就直接附上代码:

void RotateRL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;RotateR(parent->_right);RotateL(parent);if (bf == 0){subR->_bf = 0;subRL->_bf = 0;parent->_bf = 0;}else if (bf == 1){subRL->_bf = 0;parent->_bf = -1;subR->_bf = 0;}else if (bf == -1){subLR->_bf = 0;subR->_bf = 1;parent->_bf = 0;}else{assert(false);}}

 5.插入代码总结

bool insert(const pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(kv);if (parent->_kv.first < kv.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;//更新平衡因子while (parent){//定义平衡因子左减右加if (cur == parent->_left)parent->_bf--;elseparent->_bf++;if (parent->_bf == 0){//更新结果break;}else if (parent->_bf == 1 || parent->_bf == -1){//继续向上更新cur = parent;parent = parent->_parent;}else if (parent->_bf == 2 || parent->_bf == -2){//旋转处理不平衡if (parent->_bf == 2 && cur->_bf == 1)//左单旋{RotateL(parent);}else if (parent->_bf == 2 && cur->_bf == -1)//右左双旋{RotateRL(parent);}else if (parent->_bf == -2 && cur->_bf == 1)//左右双旋{RotateLR(parent);}else if(parent->_bf == -2 && cur->_bf == -1)//右单旋{RotateR(parent);}break;}else{//说明传入的有问题报错日志assert(false);}}return true;}//右单旋void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;Node* parentParent = parent->_parent;subL->_right = parent;parent->_parent = subL;//parent有可能是整棵树的根,也有可能是局部子树//如果是整棵树的根则修改root//if (parentParent == nullptr){_root = subL;subL->_parent = nullptr;}else{if (parent == parentParent->_left){parentParent->_left = subL;}else{parentParent->_right = subL;}subL->_parent = parentParent;}parent->_bf = subL->_bf = 0;}//左单旋void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;Node* parentParent = parent->_parent;subR->_left = parent;parent->_parent = subR;if (parentParent == nullptr){_root = subR;subR->_parent = nullptr;}else{if (parent == parentParent->_left){parentParent->_left = subR;}else{parentParent->_right = subR;}subR->_parent = parentParent;}parent->_bf = subR->_bf = 0;}void RotateLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;RotateL(parent->_left);RotateR(parent);if (bf == 0){subL->_bf = 0;subLR->_bf = 0;parent->_bf = 0;}else if (bf == 1){subL->_bf = -1;parent->_bf = 0;subLR->_bf = 0;}else if (bf == -1){subL->_bf = 0;subLR->_bf = 0;parent->_bf = 1;}else{assert(false);}}void RotateRL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;RotateR(parent->_right);RotateL(parent);if (bf == 0){subR->_bf = 0;subRL->_bf = 0;parent->_bf = 0;}else if (bf == 1){subRL->_bf = 0;parent->_bf = -1;subR->_bf = 0;}else if (bf == -1){subLR->_bf = 0;subR->_bf = 1;parent->_bf = 0;}else{assert(false);}}

3.键值查找  树的高度计算 与平衡树判断

查找值时,只需要遍历整个树即可,以根节点(cur)为起始点,当所找的值比cur小时向左树寻找,比cur大时向右树寻找,直到cur值等于寻找值时返回当前结点,若cur为空了仍未找到结点就返回空。

Node* find(const K& key){Node* cur = _root;while (cur){if (cur->_kv.first < key){cur = cur->_right;}else if (cur->_kv.first > key){cur = cur->_left;}else{return cur;}}return nullptr;}

计算树的高度时我们采用递归左右子树的方法实现,判断左右子树的大小取大值,逐层递归得到最终值。

int _Height(Node* root){if (root == nullptr)return 0;int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;}

 判断是否平衡时,我们可以调用左右子树的高度,然后相减,得到diff。若diff大于2说明高度异常,若根结点的bf不等于diff说明平衡因子计算存在错误。

bool _IsBalanceTree(Node* root){if (nullptr == root)return true;int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);int diff = leftHeight - rightHeight;if (abs(diff) >= 2){cout << root->_kv.first << "高度异常" << endl;}if (root->_bf != diff){cout << root->_kv.first << "平衡因子异常" << endl;return false;}return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);}

 三.总结

平衡树要求任意节点的左右子树高度差绝对值不超过 1,且左右子树本身也是平衡树。通过限制树的高度(保持在 O(logn) 级别),确保查找、插入、删除等操作的时间复杂度稳定在 O(logn),避免退化为链表的 O(n) 复杂度。每次插入或删除后通过旋转(左旋、右旋、左右双旋、右左双旋)立即恢复平衡。

它的优点在于时间复杂度低,适合大量数据的操作,缺点在于需要动态的更新数据进行旋转增大了时间开销。

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

相关文章:

  • 做网站banner是什么意思财经新闻每日财经报道
  • 建设网站方案友情链接你会回来感谢我
  • 为什么会有人攻击我用织梦做的网站网站平台怎么推广
  • 企业网站的一般要素有长沙哪里有网站推广优化
  • oa网站建设电商运营培训学费多少
  • 有哪些网站是做背景图片素材的如何创建自己的卡网
  • 平面设计网站中文营销策略的思路
  • 甘肃手机网站建设福州seo排名优化公司
  • 请谁做网站比较放心有域名了怎么建立网站
  • 网站开发岗位需求分析百度视频推广怎么收费
  • 织梦wap手机网站模板爱站网怎么使用
  • 有口碑的常州网站建设西安seo按天收费
  • asp美食网站源码seo优化排名软件
  • 应用小程序下载四川seo推广
  • 太原市网站制作公司怎样推广一个产品
  • 自己做外贸开通什么网站推广工具
  • 北京移动端网站宁波优化系统
  • 太原做手机网站建设公司网络推广方案
  • 那些网站是伪静态广州百度竞价托管
  • 宁津哪个网络公司做网站比较好百度seo多久能优化关键词
  • 做网站用哪个版本的eclipseseo黑帽优化
  • 512内存 wordpress网站推广优化排名seo
  • 环境没有tomcat怎么演示自己做的网站合肥网络营销公司
  • 深圳网站建设 设计怎么在百度推广自己的公司
  • 兰州网站分类导航优化推广网站排名
  • 网站建设宣传党建夜夜草
  • 刷q币网站建设制作链接的小程序
  • 做小说网站做国外域名还是国内的好处长沙百度网站优化
  • 茶叶网络推广方案找索引擎seo
  • 网站开发网页网页制作软件免费版