一、思路
1.创建AVL树:
每次输入一个数num,然后用Insert()函数,将num插入到AVL树中。
// 创建AVL平衡树
template
void AVLTree::CreateAVLTree() {
T num;
cout << "请输入数(以-1结束输入)建立平衡二叉树:" << endl;
while(cin >> num && num != -1)
Insert(num); // 插入到树中
}
2.插入值x:
(1)寻找插入的位置:如果AVL树已经存在该x值,则不插入。如果x小于当前结点的值,则往左子树中找插入位置;否则,往右子树找插入位置。
// 1.寻找插入位置
while(p != NULL) { // 树不为空
if(x == p->data) // 已经存在该值,不用再插入
return false;
pre = p; // 保存当前结点
st.push(pre); // 用栈记忆查找路径
if(x < p->data) // 要插入的值小于当前结点的值,往左子树继续查找
p = p->left;
else // x大于当前结点的值
p = p->right; // 往右子树查找
}
(2)把x插入到AVL平衡树中:如果x小于父结点的值,则插入到父结点的左孩子位置;否则, 插入到父结点的右孩子上。
// 2.找到了准备插入的位置,把x插入到AVL平衡树中
p = new AVLNode(); // 创建新结点
p->data = x; // 存储要插入的值
p->bf = 0; // 新结点,平衡因子为0
p->left = p->right = NULL;
if(p == NULL) {
cerr << "存储空间分配失败!" << endl;
exit(1);
}
if(pre == NULL) { // 空树,新结点成为根结点
root = p;
return true;
}
if(x < pre->data) // 插入到左子树
pre->left = p;
else
pre->right = p; // 插入到右子树
(3)重新平衡化AVL树:因为插入的新结点,可能导致AVL树平衡被破坏。
从插入的新结点的父结点开始往根结点调整。如果当前结点是父结点的左孩子,则父结点的平衡因子要减1,如果当前结点是父结点的右孩子,则父结点的平衡因子要加1。
然后,通过父结点parent的平衡因子来决定是否需要平衡化旋转:
A. 父结点parent的平衡因子为0。说明刚才是在parent的较矮的子树上插入了新结点,父结点处平衡,且高度没有增减。此时从parent到根的路径上各结点为根的子树高度不变,从而各个结点的平衡因子不变,可以结束重新平衡化的处理。
B.父结点parent的平衡因子的绝对值|bf|=1。说明插入前parent结点的平衡因子为0,插入新结点后,以parent为根的子树没有失去平衡,不需要平衡化旋转。但该子树的高度增加,还需从结点parent向根的方向回溯,继续考查结点parent的父结点的平衡状态。
C.父结点parent的平衡因子的绝对值|bf|=2。说明新结点在较高的子树上插入,造成了不平衡,需要做平衡化旋转。旋转要分情况讨论。旋转后以parent为根的子树高度降低,因此无需继续向上层回溯。
// 3.重新平衡化AVL树
while(st.empty() == false) { // 栈不空
pre = st.top(); // 取栈顶元素
st.pop();
if(p == pre->left) // 如果是左孩子结点,则父结点平衡因子减1
pre->bf--; // 调整父结点的平衡因子
else
pre->bf++; // 如果是右孩子结点,则父结点平衡因子加1
if(pre->bf == 0) // 第1种情况,平衡退出
break;
else if(pre->bf == 1 || pre->bf == -1) // 第2种情况,|bf|=1
p = pre; // 往上继续考察父结点的平衡状态
else { // 第3中情况,|bf| = 2,需要做平衡处理
d = (pre->bf < 0) ? -1 : 1; // 区别单双旋转标志
if(p->bf == d) { // 两结点平衡因子同号,单旋转
if(d == -1)
RotateR(pre); // 右单旋转
else
RotateL(pre); // 左单旋转
} else { // 两结点平衡因子反号,双旋转
if(d == -1)
RotateLR(pre); // 先左后右双旋转
else
RotateRL(pre); // 先右后左双旋转
}
break; // 不再往上调整
} // else
} // while
(4)把调整的子树,重新链接到原树中
// 调整到树的根结点,根结点变为当前的pre
if(st.empty())
root = pre;
else { // 中间重新链接,说明是在根结点的子树中进行平衡化
// 平衡化的子树,子树的父结点已丢失,要与原树重新进行链接,所以要取栈顶元素,
// 然后挂入到栈顶元素的孩子树上。
q = st.top(); // 获取栈顶元素
if(q->data > pre->data)
q->left = pre;
else
q->right = pre;
}
3.删除:
(1)查找要删除的位置:
while(p != NULL) { // 1.寻找删除位置
if(x == p->data) // 找到了,停止搜索
break;
pre = p;
st.push(pre); // 用栈记忆查找路径
if(x < p->data) // 继续往左子树查找插入位置
p = p->left;
else // 继续往右子树查找插入位置
p = p->right;
}
(2)找到位置后,判断当前要删除的结点p有两个子女,还是最多只有一个子女:
A.p有两个子女:
首先搜索p在中序次序下的直接前驱q(同样可以找直接后继),再把结点q的内容传送给结点p,现在问题转移到删除结点q。把结点q当作被删结点p,它是只有一个子女的结点。
if(p->left != NULL && p->right != NULL) { // 被删除结点有左右子女
pre = p;
st.push(pre);
q = p->left; // 往p左子树找p的直接前驱
while(q->right != NULL) {
pre = q;
st.push(pre);
q = q->right;
}
p->data = q->data; // 用q的值填补p
p = q; // 被删除结点转为q
}
B.如果被删结点p最多只有一个子女q:
可以简单地把p的父结点pre中原来指向p的指针改指到q;如果p没有子女,p的父结点pre的相应位置为 NULL。然后将原来以结点pre为根的子树的高度减1,并沿pre通向根的路径反向追踪高度的这一变化对路径上各个结点的影响。
if(p->left != NULL) // 被删除结点p只有一个子女q
q = p->left;
else
q = p->right;
考查结点q的父结点pre:
如果q是pre的左子女,则pre的平衡因子应当加1(应为被删除的结点p为pre的原左子女),否则减少1。
(1)pre的平衡因子原来为0,在它的左子树或右子树被缩短后,则它的平衡因子改为1或-1。由于以pre为根的子树高度没有改变,从pre到根结点的路径上所有结点都不需要调整。此时可结束本次被删除的重新平衡过程。
(2)结点pre的平衡因子原不为0,且较高的子树被缩短,则p的平衡因子改为0。此时以pre为根的子树平衡,但其高度减1。为此需要继续考查结点pre的父结点的平衡状态。
(3)结点pre的平衡因子原不为0,且较矮的子树又被缩短,则在结点pre发生不平衡。为此需进行平衡旋转来恢复平衡。令pre的较高的子树的根为q(该子树未被缩短)。然后,根据q的平衡因子,进行以下操作:
A.第一种情况:pre平衡因子为-2或2,对应的子女q(以q为根的子树原左右子女高度相同)的平衡因子为0,做单旋转即可。
B.第二种情况,pre平衡因子为-2或2,对应的子女q(以q为根的子树原左右子女高度差1)的平衡因子为-1或1,同号则单旋转,不同号则左右双旋转
二、实现程序:
1.AVLTree.h
#ifndef AVLTree_h
#define AVLTree_h
#include
#include
using namespace std;
template
struct AVLNode { // AVL树结点结构的定义
T data; // 存储数据
int bf; // 平衡因子:-1, 0, 1
AVLNode *left, *right; // 左右子结点
};
template
class AVLTree { // AVL树的类定义
public:
AVLTree(); // 构造函数
~AVLTree();// 析构函数
void CreateAVLTree(); // 创建AVL平衡树
bool Insert(T x); // 插入值x
bool Remove(T x); // 删除值x
void Traverse(); // 中序遍历AVL树
// int Height() const; // 求高度
bool Find(T x); // 查找x
private:
AVLNode *root; // 根结点
void RotateL(AVLNode *&ptr); // 左单旋
void RotateR(AVLNode *&ptr); // 右单旋
void RotateLR(AVLNode *&ptr); // 先左后右双旋
void RotateRL(AVLNode *&ptr); // 先右后左双旋
void Traverse(AVLNode *&ptr); // 中序遍历AVL树
};
// 构造函数
template
AVLTree::AVLTree() {
root = NULL;
}
// 析构函数
template
AVLTree::~AVLTree() {
}
// 创建AVL平衡树
template
void AVLTree::CreateAVLTree() {
T num;
cout << "请输入数(以-1结束输入)建立平衡二叉树:" << endl;
while(cin >> num && num != -1)
Insert(num); // 插入到树中
}
// 在以ptr为根的AVL树中插入新元素x, 如果插入成功,函数返回true,否则返回false
template
bool AVLTree::Insert(T x) {
int d;
AVLNode *pre = NULL, *p = root, *q;
stack *> st; // 用栈记忆查找路径
// 1.寻找插入位置
while(p != NULL) { // 树不为空
if(x == p->data) // 已经存在该值,不用再插入
return false;
pre = p; // 保存当前结点
st.push(pre); // 用栈记忆查找路径
if(x < p->data) // 要插入的值小于当前结点的值,往左子树继续查找
p = p->left;
else // x大于当前结点的值
p = p->right; // 往右子树查找
}
// 2.找到了准备插入的位置,把x插入到AVL平衡树中
p = new AVLNode(); // 创建新结点
p->data = x; // 存储要插入的值
p->bf = 0; // 新结点,平衡因子为0
p->left = p->right = NULL;
if(p == NULL) {
cerr << "存储空间分配失败!" << endl;
exit(1);
}
if(pre == NULL) { // 空树,新结点成为根结点
root = p;
return true;
}
if(x < pre->data) // 插入到左子树
pre->left = p;
else
pre->right = p; // 插入到右子树
// 3.重新平衡化AVL树
while(st.empty() == false) { // 栈不空
pre = st.top(); // 取栈顶元素
st.pop();
if(p == pre->left) // 如果是左孩子结点,则父结点平衡因子减1
pre->bf--; // 调整父结点的平衡因子
else
pre->bf++; // 如果是右孩子结点,则父结点平衡因子加1
if(pre->bf == 0) // 第1种情况,平衡退出
break;
else if(pre->bf == 1 || pre->bf == -1) // 第2种情况,|bf|=1
p = pre; // 往上继续考察父结点的平衡状态
else { // 第3中情况,|bf| = 2,需要做平衡处理
d = (pre->bf < 0) ? -1 : 1; // 区别单双旋转标志
if(p->bf == d) { // 两结点平衡因子同号,单旋转
if(d == -1)
RotateR(pre); // 右单旋转
else
RotateL(pre); // 左单旋转
} else { // 两结点平衡因子反号,双旋转
if(d == -1)
RotateLR(pre); // 先左后右双旋转
else
RotateRL(pre); // 先右后左双旋转
}
break; // 不再往上调整
} // else
} // while
// 调整到树的根结点,根结点变为当前的pre
if(st.empty())
root = pre;
else { // 中间重新链接,说明是在根结点的子树中进行平衡化
// 平衡化的子树,子树的父结点已丢失,要与原树重新进行链接,所以要取栈顶元素,
// 然后挂入到栈顶元素的孩子树上。
q = st.top(); // 获取栈顶元素
if(q->data > pre->data)
q->left = pre;
else
q->right = pre;
}
return true;
}
// 中序遍历AVL树
template
void AVLTree::Traverse() {
Traverse(root);
}
// 中序遍历AVL树
template
void AVLTree::Traverse(AVLNode *&ptr) {
if(ptr != NULL) {
Traverse(ptr->left); // 中序遍历左子树
cout << ptr->data << " " ; // 输出数据
Traverse(ptr->right); // 中序遍历右子树
}
}
// 删除x
template
bool AVLTree::Remove(T x) {
AVLNode *pre = NULL, *p = root, *q, *ppre;
int d, dd = 0;
stack*> st;
while(p != NULL) { // 1.寻找删除位置
if(x == p->data) // 找到了,停止搜索
break;
pre = p;
st.push(pre); // 用栈记忆查找路径
if(x < p->data) // 继续往左子树查找插入位置
p = p->left;
else // 继续往右子树查找插入位置
p = p->right;
}
if(p == NULL) // 未查找到被删结点,删除失败
return false;
if(p->left != NULL && p->right != NULL) { // 被删除结点有左右子女
pre = p;
st.push(pre);
q = p->left; // 往p左子树找p的直接前驱
while(q->right != NULL) {
pre = q;
st.push(pre);
q = q->right;
}
p->data = q->data; // 用q的值填补p
p = q; // 被删除结点转为q
}
if(p->left != NULL) // 被删除结点p只有一个子女q
q = p->left;
else
q = p->right;
if(pre == NULL) // 被删结点为根结点,需要将root指向被删结点的子女q
root = q;
else { // 被删结点不是根结点
if(pre->left == p) // 被删结点p为父结点pre的左子女
pre->left = q; // 则把父结点pre的左子女变为被删结点p的子女q
else
pre->right = q;
while(!st.empty()) { // 重新平衡化
pre = st.top();
st.pop(); // 从栈中退出父结点
if(pre->right == q)
pre->bf--; // 调整父结点的平衡因子
else
pre->bf++;
if(!st.empty()) {
ppre = st.top(); // 从栈中取出祖父结点
dd = (ppre->left == pre) ? -1 : 1; // 旋转后与上层链接方向
} else // 栈空,旋转后不与上层链接
dd = 0;
if(pre->bf == 1 || pre->bf == -1) // |bf| = 1
break;
if(pre->bf != 0) { // |bf|=2
if(pre->bf < 0) {
d = -1;
q = pre->left;
} else {
d = 1;
q = pre->right; // 用q指示较高的子树
}
// 第一种情况:pre平衡因子为-2或2,对应的子女q(以q为根的子树原左右子女高度相同)的平衡因子为0,做单旋转即可。
if(q->bf == 0) { // 较高的子树平衡因子为0
if(d == -1) { // 较高的子树是在父结点pre的左子树上,需要做右单旋转
RotateR(pre);
pre->bf = 1;
pre->left->bf = -1;
} else { // 较高的子树是在父结点pre的右子树上,需要做左单旋转
RotateL(pre);
pre->bf = -1;
pre->right->bf = 1;
}
break;
}
// 第二种情况,pre平衡因子为-2或2,对应的子女q(以q为根的子树原左右子女高度差1)的平衡因子为-1或1,同号则单旋转,不同号则左右双旋转
if(q->bf == d) { // 两结点平衡因子同号
if(d == -1)
RotateR(pre); // 右单旋转
else
RotateL(pre); // 左单旋转
} else { // 两结点平衡因子反号
if(d == -1)
RotateLR(pre); // 先左后右双旋转
else
RotateRL(pre); // 先右后左双旋转
}
if(dd == -1) // 新根称为祖父结点ppre的左子女
ppre->left = pre;
else if(dd == 1) // 新根称为祖父结点ppre的右子女
ppre->right = pre; // 旋转后新根与上层链接
}
q = pre; // |bf| = 0;以pre根结点的高度减1,要往上,调整祖先结点的平衡因子
} // while结束
if(st.empty())
root = pre; // 调整到树的根结点
} // else结束
delete p;
return true;
}
// 左单旋
template
void AVLTree::RotateL(AVLNode *&ptr) {
AVLNode *subL = ptr; // 要左旋转的结点
ptr = subL->right; // 原根的右子女
subL->right = ptr->left; // ptr成为新根前卸掉左边负载
ptr->left = subL; // 左单旋转,ptr为新根
ptr->bf = subL->bf = 0; // 平衡因子变为0
}
// 右单旋
template
void AVLTree::RotateR(AVLNode *&ptr) {
AVLNode *subR = ptr; // 要右旋转的结点
ptr = subR->left; // 原根的左子女
subR->left = ptr->right; // ptr成为新根前卸掉右边负载
ptr->right = subR; // 右单旋转,ptr为新根
ptr->bf = subR->bf = 0; // 平衡因子变为0
}
// 先左后右双旋
template
void AVLTree::RotateLR(AVLNode *&ptr) {
// 原根结点,准备旋转为右结点;原根的左结点旋转还是为左结点
AVLNode *subR = ptr, *subL = subR->left;
ptr = subL->right; // 原根的左孩子结点的右孩子结点旋转为新根结点
subL->right = ptr->left; // ptr成为新根前甩掉它左边的负载
ptr->left = subL; // 左单旋转,ptr成为新根
if(ptr->bf <= 0) // 插入新结点后,ptr左子树变高
subL->bf = 0;
else // 在ptr右边插入新结点,subL的左孩子树比右孩子树高一层
subL->bf = -1;
subR->left = ptr->right; // ptr成为新根前甩掉它右边的负载
ptr->right = subR; // 右单旋转,ptr成为新根
if(ptr->bf == -1) // 是在ptr的左孩子树插入
subR->bf = 1;
else // 在ptr的右孩子树插入
subR->bf = 0;
ptr->bf = 0;
}
// 先右后左双旋
template
void AVLTree::RotateRL(AVLNode *&ptr) {
// 原根结点,准备旋转为右结点;原根的左结点旋转还是为左结点
AVLNode *subL = ptr, *subR = subL->right;
ptr = subR->left; // 原根的右孩子结点的左孩子结点旋转为新根结点
subR->left = ptr->right; // ptr成为新根前甩掉它右边的负载
ptr->right = subR; // 右单旋转,ptr成为新根
if(ptr->bf >= 0) // 在ptr的右孩子树插入新结点
subR->bf = 0;
else // 在ptr的左孩子树插入新结点
subR->bf = 1;
subL->right = ptr->left; // ptr成为新根前甩掉它左边的负载
ptr->left = subL; // 左单旋转,ptr成为新根
if(ptr->bf == 1) // // 在ptr的右孩子树插入新结点
subL->bf = -1;
else // 在ptr的左孩子树插入新结点
subL->bf = 0;
ptr->bf = 0;
}
// 查找x
template
bool AVLTree::Find(T x) {
AVLNode *p = root;
while(p != NULL) { // 树不为空
if(x == p->data) // 已经存在该值,不用再插入
return true;
if(x < p->data) // 要插入的值小于当前结点的值,往左子树继续查找
p = p->left;
else // x大于当前结点的值
p = p->right; // 往右子树查找
}
return false; // 没找到
}
#endif /* AVLTree_h */
2.main.cpp
// 二叉平衡树:
// (1)建立
// (2)插入
// (3)删除
// (4)查找
// (5)中序遍历
// 测试数据:16 3 7 11 9 26 18 14 15 -1
#include "AVLTree.h"
int main(int argc, const char * argv[]) {
AVLTree st; // 创建AVL树对象
bool finished = false;
int choice, x;
while(!finished) {
cout << "[1]创建AVL树" << endl;
cout << "[2]中序遍历" << endl;
cout << "[3]删除" << endl;
cout << "[4]查找某个值x:" << endl;
cout << "[5]退出" << endl;
cout << "请输入你的选择[1-5]:" << endl;
cin >> choice;
switch(choice) {
case 1:
st.CreateAVLTree();
break;
case 2:
st.Traverse();
cout << endl;
break;
case 3:
cout << "请输入要删除的值x:";
cin >> x;
st.Remove(x);
break;
case 4:
cout << "请输入要查找的值x:";
cin >> x;
if(st.Find(x))
cout << "查找到了" << x << endl;
else
cout << "未找到" << x << endl;
break;
case 5:
finished = true;
break;
default:
cout << "输入选择错误!" << endl;
}
}
return 0;
}
测试结果: