树是存储常用的一种结构,合理的使用树可以让我们很方便的存入或者删除数据或者将存入的东西很方便的使用。
二叉搜索树是一种很不错的方便搜索的树,树的算法很简单,假设这颗树是由某个数据结构的节点创建的,根据这个节点的比较算法,比根节点大的节点,应该插入到根节点的右子树中,否则将他插入到左子树中。想必大家都看明白了,这是一个递归算法,层层递归最终找到要插入的位置进行插入。
这样可以大大节约搜索时间,一个类似二分查找的时间复杂度。但是这颗树再某些情况不是特别好用,假设插入时按照升序或者降序插入,或者左右树高度差很悬殊的情况下,那么这颗树便成了一个类似于链表的结构,这样查找的时间复杂度还是O(N),这是我们不想看到的,于是便有了二叉平衡搜索树。
二叉平衡搜索树满足二叉搜索树的所有性质,只是多了一个条件:左右子树高度差不超过1。这就严格控制了树的高度近似是lg N。但是实现起来就要比普通的搜索树要有点难度了。
接下来我们就来看一下这个二叉平衡搜索树是如何实现的。
#pragma once
#include <iostream>
using namespace std;
template<class K,class V>
struct AVLTreeNode
{
//三叉链式节点
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
AVLTreeNode<K, V>* _parent;
K _key;
V _value;
//平衡因子,用来表示左右树的高度差;
int _bf;
AVLTreeNode(const K& key, const V& value)
:_left(NULL),
_right(NULL),
_parent(NULL),
_key(key),
_value(value),
_bf(0)
{}
};
这是节点的结构以及构造函数。
在这我们尽量使用三叉链结构牺牲一些空间而使代码易于控制。
template<class K, class V>
class AVLTree
{
typedef AVLTreeNode<K, V> Node;
public:
AVLTree()
{}
AVLTree(const K* arrk,const V* arrv , size_t n) // 我在学习数据结构比较喜欢的构造函数,便于修改代码
{
size_t i = 0;
for (i; i < n; i++)
{
insert(arrk[i],arrv[i]);
}
}
~AVLTree()//析构函数 , destory是一个递归删除节点的算法,可以跳过不看,只是为了不产生内存泄露的问题
{
destory(_root);
}
bool isbalance()//写出来的代码我们必须要验证准确性,我们这提供了一个接口,根据平衡树的性质检查这棵树是否是一颗平衡树。
{
int depth;
return _isbalance(_root,depth);
}
bool insert(const K& key, const V& value)//这是这棵树之所以成为二叉平衡搜索树的主要算法:插入,另一个删除和这个算法是一个对称算法在这里
//没有实现
{
if (_root == NULL)
{
_root = new Node(key, value);
return true;
}
Node* cur = _root;
Node* parent = NULL;
while (cur)
{
if (value > cur->_value)
{
parent = cur;
cur = cur->_right;
}
else if (value < cur->_value)
{
parent = cur;
cur = cur->_left;
}
else
return false;
}
cur = new Node(key, value);
cur->_parent = parent;
if (value>parent->_value)
parent->_right = cur;
else
parent->_left = cur;
//走到这里已经满足二叉搜索树的性质,我们需要调节cur祖先的平衡因子,因为cur的祖先的高度可能会发生改变
while (parent)
{
if (cur == parent->_left)
parent->_bf--;
else
parent->_bf++;
if (parent->_bf == 0)//当parent的平衡因子为变为0,表示以parent为根节点的子树高度没变(仔细思考)
break;
else if (parent->_bf == 1 || parent->_bf == -1)// 这棵树的高度增加了,还需要需要继续向上调整
{
cur = parent;
parent = parent->_parent;
}
//接下来时AVL树能保证平衡的关键算法:旋转
else if (parent->_bf == 2)
{
if (cur->_bf == 1)
{
//1.左单旋
RotalL(parent);
//2.调节平衡因子
parent->_bf = 0;
cur->_bf = 0;
break;//这里也是因为插入新节点导致树的高度改变,而旋转使高度又变回了之前的样子,所以不需要调整了
}
if (cur->_bf == -1)
{
Node* subrl = cur->_left;
//1.右左双旋
RotalR(cur);
RotalL(parent);
//2.调节平衡因子
if (subrl->_bf == 0)
{
parent->_bf = 0;
cur->_bf = 0;
}
if (subrl->_bf == 1)
{
cur->_bf = 0;
parent->_bf = -1;
subrl->_bf = 0;
}
if (subrl->_bf == -1)
{
subrl->_bf = 0;
cur->_bf = 1;
parent->_bf = 0;
}
break;
}
}
else if (parent->_bf == -2)//此处与上面的完全对称,就不在此赘述。
{
if(cur->_bf == -1)
{
//1.右单旋
RotalR(parent);
parent->_bf = 0;
cur->_bf = 0;
//2.调节平衡因子
}
if (cur->_bf == 1)
{
Node* sublr = cur->_right;
//1.左右双旋
RotalL(cur);
RotalR(parent);
//2.调节平衡因子
if (sublr->_bf == 0)
{
parent->_bf = 0;
cur->_bf = 0;
}
if (sublr->_bf == 1)
{
cur->_bf = -1;
parent->_bf = 0;
sublr->_bf = 0;
}
if (sublr->_bf == -1)
{
sublr->_bf = 0;
cur->_bf = 0;
parent->_bf = 1;
}
}
}
else
{
cout << "平衡因子异常" << endl;
}
}
return true;
}
protected:
bool _isbalance(Node* root, int& depth)
{
if (root == NULL)
{
depth = 0;
return true;
}
else if (root->_left == NULL&&root->_right == NULL)
{
depth = 1;
return true;
}
int leftdepth, rightdepth;
if (_isbalance(root->_left, leftdepth) && _isbalance(root->_right, rightdepth))
depth = leftdepth > rightdepth ? leftdepth + 1 : rightdepth + 1;
else
return false;
if (root->_bf != rightdepth - leftdepth)
cout << "平衡因子异常" <<root->_value<<" "<<root->_bf <<" " <<rightdepth-leftdepth<<endl; //这样写只是为了使出错后调试
//更为方便,可以不写直接报错。
return abs(leftdepth - rightdepth) < 2;
}
void RotalL(Node* cur)
{
Node* subr = cur->_right;
Node* subrl = subr->_left;
Node* parent = cur->_parent;
if (cur == _root)
_root = subr;
else if (parent->_left == cur)
parent->_left = subr;
else
parent->_right = subr;
subr->_parent = parent;
cur->_parent = subr;
subr->_left = cur;
cur->_right = subrl;
if (subrl != NULL)
subrl->_parent = cur;
}
void RotalR(Node* cur)
{
Node* subl = cur->_left;
Node* sublr = subl->_right;
Node* parent = cur->_parent;
if (cur == _root)
_root = subl;
else if (parent->_left == cur)
parent->_left = subl;
else
parent->_right = subl;
subl->_parent = parent;
cur->_parent = subl;
subl->_right = cur;
cur->_left = sublr;
if (sublr != NULL)
sublr->_parent = cur;
}
void destory(Node* root)
{
if (root == NULL)
return;
if (root->_left == NULL&&root->_right == NULL)
{
delete root;
return;
}
else
{
destory(root->_left);
destory(root->_right);
delete root;
}
}
private:
Node* _root;
};
void TestAVLTree()
{
int a[] = { 1, 3, 8, 9, 10, 15, 2,100,400,300,500,290,360 };
AVLTree<int, int> t(a,a,sizeof(a)/sizeof(a[0]));
bool i=t.isbalance();
}
大部分算法根据注释是可以看明白的,少数算法可能看代码还是不够明确因此画了几幅图便于理解。
以上就是一些干看代码难以理解的算法图,相信大家在看了算法图之后可以将代码思路理清楚。当然了,如果有什么错误还请大佬提出来,本人也是感激不尽。