目录
一、AVL树概念
1.二叉搜索树的缺点
map/multimap/set/multiset的底层都按照二叉搜索树实现,但是在【C++】-- 搜索二叉树一文中已经了解到二叉搜索树的缺点在于,假如向树中插入的元素有序或者接近有序时,二叉搜索树就会退化成单支树,时间复杂度会退化成O(N),相当于在顺序表中搜索元素,效率低下。所以map/multimap/set/multiset的底层结构对二叉搜索树做了处理,采用平衡树来实现。
2.AVL树的概念
如何避免二叉树搜索树会退化成单支树的缺点呢?
向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。为什么高度差的绝对值不超过1而不是0呢?因为如果高度差的绝对值不超过0,那么二叉树就变成满二叉树了,因此绝对值不能超过1。这就引入了平衡二叉树的概念:
一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:
(1)它的左右子树都是AVL树
(2)左右子树高度之差(简称平衡因子=右子树高度-左子树高度)的绝对值不超过1(-1/0/1)
如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在,搜索时
间复杂度O()。
二、AVL树定义
由于要实现AVL树的增删改查,所以定义AVL树的节点,就需要定义parent,否则插入节点时,不知道要链接到树里面哪个节点下面。
1.AVL树节点定义
节点定义:
#pragma once
#include<iostream>
using namespace std;
template<class K,class V>
class AVLTreeNode
{
AVLTreeNode<K, V>* _left;//左子树
AVLTreeNode<K, V>* _right;//右子树
AVLTreeNode<K, V>* _parent;//父亲
int _bf;//平衡因子
pair<K, V> _kv;//节点
AVLTreeNode(const pair<K, V>&, kv)
{
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_bf(0)
, _kv(kv)
}
{}
};
2.AVL树定义
template<class K,class V>
struct AVLTree
{
typedef AVLTreeNode<K, V> Node;
public:
//构造函数
AVLTree()
:_root(nullptr)
{}
void _Destroy(Node* root)
{
if (root == nullptr)
{
return;
}
_Destroy(root->_left);
_Destroy(root->_right);
delete root;
}
//重载operator[]
V& operator[](const K& key)
{
pair<Node*, bool> ret = Insert(make_pair(key, V()));
return ret.first->_kv.second;
}
//析构函数
~AVLTree()
{
_Destroy(_root);
_root = nullptr;
}
private:
Node* _root;
};
三、AVL树插入
1.插入节点
插入节点需要先判断树是否为空:
(1)若为空,让该节点作为根节点
(2)若不为空,分3种情况:
①key比当前节点小,向左走
②key比当前节点大,向右走
③相等,插入失败
如果没找到节点,那么需要插入新节点
bool Insert(const pair<K, V>& kv)
{
//1.空树
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
//2.非空树
Node* parent = _root, * cur = _root;
while (cur)
{
if (cur->_kv.first > kv.first)//向左找
{
parent = cur;
cur = cur->_left;
}
else if (cur->_kv.first < kv.first)//向右找
{
parent = cur;
cur = cur->_right;
}
else//找到了
{
return false;
}
}
//没找到,需要插入
cur = new Node(kv);
if (parent->_kv.first < cur->_kv.first)
{
parent->_right = cur;
cur->_parent = parent;
}