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

国外的自建站平台是什么/电商网址

国外的自建站平台是什么,电商网址,重装 wordpress,现在由哪些网站可以做外链目录 1. AVL树的概念 2. AVL树节点定义 3. AVL树的插入 4. AVL树的旋转 4.1 左单旋 4.2 右单旋 4.3 左右双旋 4.4 右左双旋 5. AVL树的验证 6. AVL树的查找 7. AVL树的性能 1. AVL树的概念 二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜…

目录

1. AVL树的概念

2. AVL树节点定义

3. AVL树的插入

4. AVL树的旋转

4.1 左单旋

 4.2 右单旋

4.3 左右双旋 

 4.4 右左双旋

 5. AVL树的验证

 6. AVL树的查找

7. AVL树的性能


1. AVL树的概念

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查 找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii 和E.M.Landis在1962年 发明了一种解决上述问题的方法:

当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(超过1需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

AVL树可以是一棵空树,也可以是具有以下性质的一棵二叉搜索树:

  • 树的左右子树都是AVL树。
  • 树的左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)。


如果一棵二叉搜索树的高度是平衡的,它就是AVL树。如果它有n个结点,其高度可保持在O(logN),搜索时间复杂度也是O(logN)

注意: 这里所说的二叉搜索树的高度是平衡的是指,树中每个结点左右子树高度之差的绝对值不超过1(右子树高度减左子树的高度),因为只有满二叉树才能做到每个结点左右子树高度之差均为0。

2. AVL树节点定义

我们这里直接实现KV模型的AVL树(KV就是键值对),为了方便后续的操作,这里将AVL树中的结点定义为三叉链结构,并在每个结点当中引入平衡因子(右子树高度-左子树高度)。除此之外,还需编写一个构造新结点的构造函数,由于新构造结点的左右子树均为空树,于是将新构造结点的平衡因子初始设置为0即可。

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; // balance factorAVLTreeNode(const pair<K, V>& kv):_kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr),_bf(0){}
};

3. AVL树的插入

AVL树插入结点时有以下三个步骤:

  1. 按照二叉搜索树的插入方法,找到待插入位置。
  2. 找到待插入位置后,将待插入结点插入到树中。
  3. 更新平衡因子,如果出现不平衡,则需要进行旋转。

因为AVL树本身就是一棵二叉搜索树,因此寻找结点的插入位置是非常简单的,按照二叉搜索树的插入规则:

  1. 待插入结点的key值比当前结点小就插入到该结点的左子树。
  2. 待插入结点的key值比当前结点大就插入到该结点的右子树。
  3. 待插入结点的key值与当前结点的key值相等就插入失败。

如此进行下去,直到找到与待插入结点的key值相同的结点判定为插入失败,或者最终走到空树位置进行结点插入。

与二叉搜索树插入结点不同的是,AVL树插入结点后需要更新树中结点的平衡因子,因为插入新结点后可能会影响树中某些结点的平衡因子。

由于一个结点的平衡因子是否需要更新,是取决于该结点的左右子树的高度是否发生了变化,因此插入一个结点后,该结点的祖先结点的平衡因子可能需要更新。

所以我们插入结点后需要倒着往上更新平衡因子,更新规则如下:

  1. 新增结点在parent的右边,parent的平衡因子++

  2. 新增结点在parent的左边,parent的平衡因子−−

每更新完一个结点的平衡因子后,都需要进行以下判断:

  • 如果parent的平衡因子等于-1或者1,表明还需要继续往上更新平衡因子。
  • 如果parent的平衡因子等于0,表明无需继续往上更新平衡因子了。
  • 如果parent的平衡因子等于-2或者2,表明此时以parent结点为根结点的子树已经不平衡了,需要进行旋转处理。

判断理由说明:

parent更新后的平衡因子分析
-1或1

只有0经过−−/++操作后会变成-1/1,说明新结点的插入使得parent的左子树或右子树增高了,即改变了以parent为根结点的子树的高度,从而会影响parent的父结点的平衡因子,因此需要继续往上更新平衡因子。

0

只有-1/1经过++/−−操作后会变成0,说明新结点插入到了parent左右子树当中高度较矮的一棵子树,插入后使得parent左右子树的高度相等了,此操作并没有改变以parent为根结点的子树的高度,从而不会影响parent的父结点的平衡因子,因此无需继续往上更新平衡因子。

-2或2此时parent结点的左右子树高度之差的绝对值已经超过1了,不满足AVL树的要求,因此需要进行旋转处理。
注意: parent的平衡因子在更新前只可能是-1/0/1(AVL树中每个结点的左右子树高度之差的绝对值不超过1)。


说明一下: 由于我们插入结点后需要倒着往上进行平衡因子的更新,所以我们将AVL树结点的结构设置为了三叉链结构,这样我们就可以通过父指针找到其父结点,进而对其平衡因子进行更新。

若是在更新平衡因子的过程当中,出现了平衡因子为-2/2的结点,这时我们需要对以该结点为根结点的树进行旋转处理,而旋转处理分为四种,在进行分类之前我们首先需要进行以下分析:

我们将插入结点称为cur,将其父结点称为parent,那么我们更新平衡因子时第一个更新的就是parent结点的平衡因子,更新完parent结点的平衡因子后,若是需要继续往上进行平衡因子的更新,那么我们必定要执行以下逻辑:

cur = parent;
parent = parent->_parent;

这里我想说明的是:当parent的平衡因子为-2/2时,cur的平衡因子必定是-1/1而不会是0。
理由如下:
若cur的平衡因子是0,那么cur一定是新增结点,而不是上一次更新平衡因子时的parent,否则在上一次更新平衡因子时,会因为parent的平衡因子为0而停止继续往上更新。
而cur是新增结点的话,其父结点的平衡因子更新后一定是-1/0/1,而不可能是-2/2,因为新增结点最终会插入到一个空树当中,在新增结点插入前,其父结点的状态有以下两种可能:

  1. 其父结点是一个左右子树均为空的叶子结点,其平衡因子是0,新增结点插入后其平衡因子更新为-1/1。(此时cur = parent,parent等于parent->_parent,继续向上判断平衡因子)
  2. 其父结点是一个左子树或右子树为空的结点,其平衡因子是-1/1,新增结点插入到其父结点的空子树当中,使得其父结点左右子树当中较矮的一棵子树增高了,新增结点后其平衡因子更新为0。(更新为0就不继续往上判断祖先的平衡因子了)

综上所述,当parent的平衡因子为-2/2时,cur的平衡因子必定是-1/1而不会是0。

根据此结论,我们可以将旋转处理分为以下四类:

  1. 当parent的平衡因子为-2,cur的平衡因子为-1时,进行右单旋。(单纯左边高)
  2.  当parent的平衡因子为2,cur的平衡因子为1时,进行左单旋。(单纯右边高)
  3. 当parent的平衡因子为-2,cur的平衡因子为1时,进行左右双旋。(整体左边高,子树右边高)
  4. 当parent的平衡因子为2,cur的平衡因子为-1时,进行右左双旋。(整体右边高,子树左边高)

并且,在进行旋转处理后就无需继续往上更新平衡因子了,因为旋转后树的高度变为插入之前了,即树的高度没有发生变化,也就不会影响其父结点的平衡因子了。具体原因请看后面的旋转讲解。

总结:同号单旋,异号双旋

bool Insert(const pair<K, V>& kv)
{//当AVL是空数,第一次插入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--;else//插入元素在父亲右边parent->_bf++;if (parent->_bf == 0){//更新结束(新增结点把parent左右子树矮的那一边增高了,此时左右高度一致)//parent树的高度没有发生变化,不会影响其父结点及以上结点的平衡因子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){RotateR(parent);}else if (parent->_bf == 2 && cur->_bf == -1){RotateRL(parent);}else{RotateLR(parent);}//旋转后就一定平衡了,无需继续往上更新平衡因子(旋转后树高度变为插入之前了)break;}else{//在插入前树的平衡因子就有问题assert(false);}}return true;
}

4. AVL树的旋转

4.1 左单旋

旋转示意图如下:

左单旋的步骤如下:

  1. 让subR的左子树作为parent的右子树。
  2. 让parent作为subR的左子树。
  3. 让subR作为整个子树的根。
  4. 更新平衡因子。

左单旋后满足二叉搜索树的性质:

  1. subR的左子树当中结点的值本身就比parent的值大,因此可以作为parent的右子树。
  2. parent及其左子树当中结点的值本身就比subR的值小,因此可以作为subR的左子树。

可以看到,经过左单旋后,树的高度变为插入之前了,即树的高度没有发生变化,所以左单旋后无需继续往上更新平衡因子。

代码如下:

//左旋
void RotatrL(Node* parent)
{Node* parentParent = parent->_parent;Node* subR = parent->_right;Node* subRl = subR->_left;parent->_right = subRl;subR->_left = parent;//subRl可能为空,因此需要判断,不能直接更新其内保存的父节点if (subRl){subRl->_parent = parent;}parent->_parent = subR;//建立parentParent和subR之间的关系if (parentParent){if (parentParent->_left == parent){parentParent->_left = subR;}else{parentParent->_right = subR;}subR->_parent = parentParent;}else{_root = subR;}//旋转要修改parent和subR的平衡因子parent->_bf = 0;subR->_bf = 0;
}

注意: 结点是三叉链结构,改变结点关系时需要跟着改变父指针的指向。

 4.2 右单旋

右单旋的步骤如下:

  1. 让subL的右子树作为parent的左子树。
  2. 让parent作为subL的右子树。
  3. 让subL作为整个子树的根。
  4. 更新平衡因子。

右单旋后满足二叉搜索树的性质:

  1. subL的右子树当中结点的值本身就比parent的值小,因此可以作为parent的左子树。
  2. parent及其右子树当中结点的值本身就比subL的值大,因此可以作为subL的右子树。

平衡因子更新如下:

可以看到,经过右单旋后,树的高度变为插入之前了,即树的高度没有发生变化,所以右单旋后无需继续往上更新平衡因子。

代码如下:

//右旋
void RotatrR(Node* parent)
{Node* parentParent = parent->_parent;Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;subL->_right = parent;if (subLR){subLR->_parent = parent;}parent->_parent = subL;if (parentParent == nullptr){subL->_parent = nullptr;_root = subL;}else{if (parentParent->_left == parent){parentParent->_left = subL;}else{parentParent->_right = subL;}subL->_parent = parentParent;}subL->_bf = parent->_bf = 0;
}

注意: 结点是三叉链结构,改变结点关系时需要跟着改变父指针的指向。

4.3 左右双旋 

左右双旋:树整体左边高,但左侧树的子树部分是右边高(所以先对子树进行左旋在对整颗树进行右旋)

1、插入新结点

2、以30为旋转点进行左单旋。

3、以90为旋转点进行右单旋。

左右双旋的步骤如下:

  1. 以subL为旋转点进行左单旋。
  2. 以parent为旋转点进行右单旋。
  3. 更新平衡因子。

左右双旋后满足二叉搜索树的性质:

左右双旋后,实际上就是让subLR的左子树和右子树,分别作为subL和parent的右子树和左子树,再让subL和parent分别作为subLR的左右子树,最后让subLR作为整个子树的根(结合图理解)。

  1. subLR的左子树当中的结点本身就比subL的值大,因此可以作为subL的右子树。
  2. subLR的右子树当中的结点本身就比parent的值小,因此可以作为parent的左子树。
  3. 经过步骤1/2后,subL及其子树当中结点的值都就比subLR的值小,而parent及其子树当中结点的值都就比subLR的值大,因此它们可以分别作为subLR的左右子树。

左右双旋后,平衡因子的更新随着subLR原始平衡因子的不同分为以下三种情况:

1、当subLR原始平衡因子是-1时,左右双旋后parent、subL、subLR的平衡因子分别更新为1、0、0

2、当subLR原始平衡因子是1时,左右双旋后parent、subL、subLR的平衡因子分别更新为0、-1、0

1、当subLR原始平衡因子是0时,左右双旋后parent、subL、subLR的平衡因子分别更新为0、0、0

可以看到,经过左右双旋后,树的高度变为插入之前了,即树的高度没有发生变化,所以左右双旋后无需继续往上更新平衡因子

代码如下:

//先左旋在右旋
void RotateLR(Node* parent)
{Node* subL = parent->_right;Node* subLR = subL->_right;//提前记录subLR的平衡因子//因为下面双旋后subLR的平衡因子会变int bf = subLR->_bf;RotatrL(subL);RotatrR(parent);if (bf == 0){parent->_bf = 0;subL->_bf = 0;subLR->_bf = 0;}else if (bf == 1){subL->_bf = -1;subLR->_bf = 0;parent->_bf = 0;}else if (bf == -1){parent->_bf = 1;subL->_bf = 0;subLR->_bf = 0;}else{assert(false);}
}

 4.4 右左双旋

左右双旋:树整体右边高,但右侧树的子树部分是左边高(所以先对子树进行右旋在对整颗树进行左旋)

动图当中的旋转示意图如下:

1、插入新结点

2、以90为旋转点进行右单旋。

3、以30为旋转点进行左单旋。

右左双旋的步骤如下:

  1. 以subR为旋转点进行右单旋。
  2. 以parent为旋转点进行左单旋。
  3. 更新平衡因子。

右左双旋后满足二叉搜索树的性质:

右左双旋后,实际上就是让subRL的左子树和右子树,分别作为parent和subR的右子树和左子树,再让parent和subR分别作为subRL的左右子树,最后让subRL作为整个子树的根(结合图理解)。

  1. subRL的左子树当中的结点本身就比parent的值大,因此可以作为parent的右子树。
  2. subRL的右子树当中的结点本身就比subR的值小,因此可以作为subR的左子树。
  3. 经过步骤1/2后,parent及其子树当中结点的值都就比subRL的值小,而subR及其子树当中结点的值都就比subRL的值大,因此它们可以分别作为subRL的左右子树。

右左双旋后,平衡因子的更新随着subLR原始平衡因子的不同分为以下三种情况:

1、当subRL原始平衡因子是1时,左右双旋后parent、subR、subRL的平衡因子分别更新为-1、0、0

2、当subRL原始平衡因子是-1时,左右双旋后parent、subR、subRL的平衡因子分别更新为0、1、0

3、当subRL原始平衡因子是0时,左右双旋后parent、subR、subRL的平衡因子分别更新为0、0、0

可以看到,经过右左双旋后,树的高度变为插入之前了,即树的高度没有发生变化,所以右左双旋后无需继续往上更新平衡因子。

//先右旋在左旋
void RotateRL(Node* parent)
{Node* subR = parent->_right;Node* subRL = subR->_left;//提前记录subRL的平衡因子//因为下面双旋后subRL的平衡因子会变int bf = subRL->_bf;RotatrR(subR);RotatrL(parent);if (bf == 0){parent->_bf = 0;subR->_bf = 0;subRL->_bf = 0;}else if (bf == 1){subRL->_bf = 0;subR->_bf = 0;parent->_bf = -1;}else if (bf == -1){subRL->_bf = 0;parent->_bf = 0;subR->_bf = 1;}else{assert(false);}
}

 5. AVL树的验证

AVL树是在二叉搜索树的基础上加入了平衡性的限制,也就是说AVL树也是二叉搜索树,因此我们可以先获取二叉树的中序遍历序列,来判断二叉树是否为二叉搜索树。

代码如下:

//中序遍历
void Inorder()
{_Inorder(_root);
}
//中序遍历子函数
void _Inorder(Node* root)
{if (root == nullptr)return;_Inorder(root->_left);cout << root->_kv.first << " ";_Inorder(root->_right);
}

 _Inorder在类中要用private修饰,这样类外对象只能调用Inorder,而为什么要这样做呢?

因为_root在类中是被private修饰的,所以我们这边其实就是套了一层壳

但中序有序只能证明是二叉搜索树,要证明二叉树是AVL树还需验证二叉树的平衡性,在该过程中我们可以顺便检查每个结点当中平衡因子是否正确。

采用后序遍历,变量步骤如下:

  1. 从叶子结点处开始计算每课子树的高度。(每棵子树的高度 = 左右子树中高度的较大值 + 1)
  2. 先判断左子树是否是平衡二叉树。
  3. 再判断右子树是否是平衡二叉树。
  4. 若左右子树均为平衡二叉树,则返回当前子树的高度给上一层,继续判断上一层的子树是否是平衡二叉树,直到判断到根为止。(若判断过程中,某一棵子树不是平衡二叉树,则该树也就不是平衡二叉树了)

代码如下:

bool IsBalanceTree()
{return _IsBalanceTree(_root);
}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;
}bool _IsBalanceTree(Node* root)
{if (root == nullptr)return true;//计算左右子树的高度int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);int diff = rightHeight - leftHeight;if (diff > 1 || diff < -1){cout << root->_kv.first <<"高度出问题";}if (diff != root->_bf){cout << root->_kv.first << "平衡因子出问题";cout << endl;}return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);
}
Node* _root = nullptr; 
};

 6. AVL树的查找

AVL树的查找函数与二叉搜索树的查找方式一模一样,逻辑如下:

  1. 若树为空树,则查找失败,返回nullptr。
  2. 若key值小于当前结点的值,则应该在该结点的左子树当中进行查找。
  3. 若key值大于当前结点的值,则应该在该结点的右子树当中进行查找。
  4. 若key值等于当前结点的值,则查找成功,返回对应结点。

代码如下:

//查找函数
Node* Find(const K& key)
{Node* cur = _root;while (cur){if (key < cur->_kv.first) //key值小于该结点的值{cur = cur->_left; //在该结点的左子树当中查找}else if (key > cur->_kv.first) //key值大于该结点的值{cur = cur->_right; //在该结点的右子树当中查找}else //找到了目标结点{return cur; //返回该结点}}return nullptr; //查找失败
}

7. AVL树的性能

AVL树是一棵绝对平衡的二叉搜索树,其要求每个结点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即logN。但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。
因此,如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但当一个结构经常需要被修改时,AVL树就不太适合了。

    http://www.whsansanxincailiao.cn/news/31943658.html

    相关文章:

  1. 个体户营业执照可以网站备案/百度软件商店
  2. 惠州品牌网站建设公司哪里有/今日国内新闻最新消息
  3. 基于html5个人网站设计论文/百度地图收录提交入口
  4. win系统和mac那个做网站好/友情链接名词解释
  5. 专业旅游网站建设/软文推广哪个平台好
  6. 临沂网站建设推广/长沙网络优化产品
  7. 做网站要多大空间/重庆网站搜索排名
  8. 网站左侧浮动代码/竞价托管怎么做
  9. 杭州注册公司代办费用/上海seo服务外包公司
  10. 制作网站 优帮云/重庆seo推广
  11. b站视频推广网站动漫/it培训机构口碑排名
  12. 盛泽做网站的/拉新推广赚钱的app
  13. 12306网站 谁做的/公司业务推广
  14. 网站建设成本分析/怎么自己弄一个网站
  15. flash制作动画教程/重庆百度快照优化
  16. 建网站后如何维护/东莞做网页建站公司
  17. 广州专业网站建设网页设计服务/产品软文范例800字
  18. 成都网站seo公司/python培训
  19. 网站 建设 价格表/百度pc网页版入口
  20. 我想弄个自己的卖货网站怎样做/外贸网络推广经验
  21. 网站页面是自己做还是使用模板/百度怎么做关键词优化
  22. 媒体网站/昆明seo博客
  23. 网站开发w亿玛酷1专注/海外seo网站推广
  24. 江西网站建设/凡科官网免费制作小程序
  25. 网站建设专业简介/重庆的seo服务公司
  26. 如何建立和设置公司网站/网站优化排名易下拉稳定
  27. 网站域名切换/软文营销网
  28. 湖北网站建设搭建/最新新闻播报
  29. 中文免费网站模板/站内优化怎么做
  30. 上网建站推广/网站排名优化客服