市场seo是什么南京关键词优化服务
AVL:
二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家 G.M.Adelson-Velskii 和 E.M.Landis 在 1962 年发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过 1 (需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。
一棵 AVL 树或者是空树,或者是具有以下性质的二叉搜索树:
- 它的左右子树都是 AVL 树
- 左右子树高度之差 (简称平衡因子) 的绝对值不超过 1 (-1/0/1)
AVL 树的性能
AVL 树是绝对平衡的二叉搜索树,要求每个节点左右子树高度差绝对值不超 1,能保证查询时高效的时间复杂度,即log2(N) 。不过对其做结构修改操作性能低下,插入时为维护平衡旋转次数多,删除时甚至可能让旋转持续到根节点。因此,若需查询高效且有序,数据个数静态(不会改变)的数据结构,可考虑 AVL 树;若结构经常修改,则不太适合 。
AVL 树适用于对数据查找、插入和删除操作性能要求较高,且数据动态变化的场景。以下是几个实用的例子:
- 数据库索引:数据库系统经常需要快速查找、插入和更新数据。例如,在一个存储用户信息的数据库表中,以用户 ID 作为关键字建立 AVL 树索引。当查询某个特定用户的信息时,能通过 AVL 树快速定位到对应的记录,时间复杂度为O(logn)。而且,当有新用户注册或用户信息发生变更时,AVL 树可以高效地进行插入和更新操作,并通过自动调整保持平衡,确保索引的性能稳定。
- 编译器符号表管理:在编译器中,符号表用于存储程序中定义的变量、函数等符号的信息。在编译过程中,需要频繁地查找符号以检查其是否已定义、获取其相关属性,同时在声明新符号时要插入到符号表中。使用 AVL 树来管理符号表,可以快速实现这些操作。例如,当编译一段包含大量变量定义和引用的代码时,AVL 树能够高效地处理符号的查找和插入,帮助编译器快速完成语法分析和语义检查等工作。
- 内存管理系统:在操作系统的内存管理中,需要跟踪内存块的分配和释放情况。可以使用 AVL 树来维护一个空闲内存块的列表,以内存块的地址或大小作为关键字。当进程请求内存时,能快速在 AVL 树中找到合适的空闲内存块进行分配;当进程释放内存时,又能将释放的内存块插入到 AVL 树中,并通过调整保持树的平衡。这样可以有效地提高内存分配和回收的效率,避免内存碎片的产生。
- 搜索引擎:在搜索引擎的索引结构中,AVL 树可以用于存储单词到文档的映射关系。例如,对于每个单词,将包含该单词的文档 ID 作为节点值构建 AVL 树。当用户输入查询关键词时,搜索引擎可以迅速在 AVL 树中查找相关单词,并获取包含该单词的文档列表,实现快速的信息检索。同时,当有新文档被索引或者文档中的单词发生变化时,AVL 树能够方便地进行插入和更新操作,保证索引的准确性和时效性。
完整代码:
#include<iostream>
using namespace std;
#include<assert.h>template<class K,class V>
struct AVLnode
{AVLnode<K, V>* _left;AVLnode<K, V>* _right;AVLnode<K, V>* _parent;pair<K, V> _kv;int _bf;AVLnode(const pair<K,V> &kv):_kv(kv),_left(nullptr),_right(nullptr),_parent(nullptr){}
};template<class K,class V>
class AVLtree
{typedef AVLnode<K, V> node;
public:bool insert(pair<K, V> kv){if (_root == nullptr){node* newnode = new node(kv);return true;}node* cur = _root;node* parent = nullptr;while (cur){parent = cur;if (cur->_kv.first < kv.first){cur = cur->_right;}else if (cur->_kv.first > kv.first){cur = cur->_left;}else{return false;}}cur = new node(kv);(parent->_kv.first > kv.first) ? (parent->_left = cur) : (parent->_right = cur);//不同于二叉搜索树的是avl是相当于双向的,所以在这个完成后还要进行一个反向的指向cur->_parent = parent;//关于这里面用parent是因为平衡因子的调整是向上调整的,//当parent走到的头结点,头结点再往上没有parent了直接为空此时就算是调整完成while (parent){if (cur == parent->_left){parent->_bf--;}else{parent->_bf++;}if (parent->_bf == 0){break;}else if (parent->_bf == 1 || parent->_bf == -1){cur = parent;parent = parent->_parent;}else//这个else就要涉及到旋转调整问题!{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 if (parent->_bf == -2 && cur->_bf == 1){RotateLR(parent);}break;}}return true;}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 find(const K& key){if (_root == nullptr){return false;}node* cur = _root;while (cur){if(cur->_kv.first>key){cur = cur->_left;}else if (cur->_kv.first < key){cur = cur->_right;}else{return true;}}return false;}bool IsBalance(){return _IsBalance(_root);}void inorder(){cout << "中序遍历:" << " ";_inorder(_root);}size_t size(){return _size(_root);}private:size_t _size(node* root){if (root == nullptr)return nullptr;return _size(root->_left) + _size(root->_right) + 1;}void _inorder(node* root){if (root == nullptr)return;_inorder(root->_left);cout << "first: " << root->_kv.first << " : " << root->_kv.second << " ";_inorder(root->_right);}bool _IsBalance(Node* root){if (root == nullptr)return true;int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);if (rightHeight - leftHeight != root->_bf){cout << root->_kv.firs << "平衡因子异常" << endl;return false;}return abs(rightHeight - leftHeight) < 2&&_IsBalance(root->_left)&&_IsBalance(root->_right);}void RotateL(node* parent){node* p_parent = parent->_parent;node* subR = parent->_right;node* subRL = subR->_right;parent->_right = subRL;subR->_left = parent;parent->_parent = subR;if (subRL)subRL->_parent = parent;if (_root == parent){_root = subR;subR->_parent = nullptr;}else{if (p_parent->_left == parent)//记住因为此时原树并没有被摧毁,parent节点也还没有更新,并不是说树的指针只能有两个指向{p_parent->_left = subR;}else{p_parent->_right = subR;}subR->_parent = p_parent;}parent->_bf = subR->_bf = 0;}void RotateR(node* parent){node* subL = parent->_left;node* subLR = subL->_right;node* p_parent = parent->_parent;subL->_right = parent;parent->_left = subLR;if (subLR){subLR->_parent = parent;}if (_root == parent){_root = subL;subL->_parent = nullptr;}else{if (p_parent->_left == parent){p_parent->_left = subL;}else if (p_parent->_right == parent){p_parent->_right = subL;}subL->_parent = p_parent;}parent->_bf = subL->_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 == 1) {parent->_bf = 0;subL->_bf = -1;subLR->_bf = 0;}else if (bf == -1) {parent->_bf = 1;subL->_bf = 0;subLR->_bf = 0;}else {parent->_bf = 0;subL->_bf = 0;subLR->_bf = 0;}}// 右左旋操作void RotateRL(node* parent){node* subR = parent->_right;node* subRL = subR->_left;int bf = subRL->_bf;RotateR(parent->_right);RotateL(parent);if (bf == 1){subRL->_bf = 0;parent->_bf = -1;subR->_bf = 0;}else if (bf == -1){parent->_bf = 0;subR->_bf = 1;subRL->_bf = 0;}else{parent->_bf = 0;subR->_bf = 0;subRL->_bf = 0;}}node* _root;};