平衡二叉树(c++)实现(存在问题:插入节点后,问题:调整树的结构存在问题)
!!版权声明:本文为博主原创文章,版权归原文作者和博客园共有,谢绝任何形式的 转载!!
作者:mohist
更新那时间: 22:13 03-02-2020 逻辑存在问题:插入节点后,调整数的结构不正确。 待修复
------ 欢迎指正-------
1、参考资料:
书籍:《算法导论》
博文:http://www.cnblogs.com/fivestudy/fivestudy/p/10340647.html 原理讲的很棒。有动画,方便理解。
这个网站可以清晰看清楚平衡二叉树的插入删除等详细过程:https://www.cs.usfca.edu/~galles/visualization/AVLtree.html
源码:

#include <iostream> using namespace std; struct node
{
int data;
int height;
node *lc;
node *rc;
node()
: data(0)
, height(0)
, lc(0)
, rc(0)
{ }
}; // 平衡二叉树
class avltree2
{
public: // 构造函数
avltree2()
: root(NULL)
{ } virtual ~avltree2(){}
//--------------------------------------------------------- // 返回结点的高度
int height(node *pnode)
{
return pnode == NULL ? 0 : pnode->height;
} // 计算两个的最大值
int max(int a, int b)
{
return a > b ? a : b;
} //------------------------------------------------------------
/*
旋转代码:
LL型
RR型
LR型
RL型
*/ // 左旋转, 函数命名方式是以二叉树插入结点的形状命名的,这里的左旋,
// 对应的形状是 RR型,即在结点右孩子插入结点。
node *rotate_rr(node *pnode)
{
node *ret_node = NULL; if (NULL != pnode)
{
// 保存失衡结点的右孩子
node *loss_balance_node_rc = pnode->rc; // 将失衡结点的右孩子的左孩子赋值给失衡结点的右孩子
pnode->rc = loss_balance_node_rc->lc; // 将失衡结点作为失衡结点的左孩子
loss_balance_node_rc->lc = pnode; // 更新失衡结点与新根结点的高度
pnode->height = max(height(pnode->lc), height(pnode->rc)) + 1;
loss_balance_node_rc->height = max( height( loss_balance_node_rc->lc),
height(loss_balance_node_rc->rc)) + 1; ret_node = loss_balance_node_rc;
} return ret_node;
} // 右旋, 函数名是以插入结点后的形式命名的,这里是
// 将新结点插入后,形成LL形状,
node *rotate_ll(node *pnode)
{
node *ret_node = NULL; if (NULL != pnode)
{
node *loss_balance_node_lc = pnode->lc; if (NULL != loss_balance_node_lc)
{
// 将失衡结点的左孩子的右孩子为失衡结点的左孩子
pnode->lc = loss_balance_node_lc->rc; // 将失衡结点作为失衡结点左孩子的右孩子
loss_balance_node_lc->rc = pnode; // 更新高度
pnode->height = max(height(pnode->lc), height(pnode->rc)) + 1; loss_balance_node_lc->height = max( height(loss_balance_node_lc->lc),
height(loss_balance_node_lc->rc)) + 1;
} ret_node = loss_balance_node_lc;
} return ret_node;
} // LR 型
// 函数命名方式同上, 需要先左旋在右旋
node* rotate_lr(node *pnode)
{
if (NULL != pnode)
{
pnode->lc = rotate_rr(pnode->lc); pnode = rotate_ll(pnode);
} return pnode;
} // RL型
// 同上,需要先右旋,再左旋
node *rotate_rl(node *pnode)
{
if (NULL != pnode)
{
// 先右旋再左旋
pnode->rc = rotate_ll(pnode->rc); pnode = rotate_rr(pnode);
} return pnode;
} //------------------------------------------------------------ // 计算pnode结点的平衡因子
int get_balance_factor(node *pnode)
{
// 平衡因子= 左孩子 - 右孩子
return (NULL == pnode) ? 0 : height(pnode->lc) - height(pnode->rc);
} // 找到以pnode为根结点的最小结点
node *get_min(node *pnode)
{
if (NULL == pnode)
return pnode; while (pnode->lc)
pnode = pnode->lc; return pnode;
} // 找到以pnode为根结点的最大结点
node *get_max(node *pnode)
{
if (NULL == pnode)
{
return pnode;
} while (pnode->rc)
pnode = pnode->rc; return pnode;
} // 插入结点
void insert(int key)
{
root = insert(root, key);
} // 删除结点值为key的结点
void remove_node(int key)
{
if (NULL == root)
return; // del_node(root, key); root = del_node2(root, key);
} // 中序遍历
void in_order_traverse()
{
if (NULL == root)
return; in_order(root);
} // 删除整棵树
void remove()
{
if (NULL == root)
return; remove(root);
} // 前序遍历
void pre_order_traverse()
{
if (NULL == root)
return; pre_order(root);
} private:
// 删除所有结点
void remove(node *pnode)
{
if (pnode)
{
remove(pnode->lc);
remove(pnode->rc);
delete pnode;
} pnode = NULL;
} // 前序遍历
void pre_order(node *pnode)
{
if (NULL != pnode)
{
cout << pnode->data << ", height = " << pnode->height << " ";
pre_order(pnode->lc);
pre_order(pnode->rc);
}
} // 中序遍历
void in_order(node *pnode)
{
if (NULL != pnode)
{
in_order(pnode->lc);
cout << pnode->data << ", height = " << pnode->height << " ";
in_order(pnode->rc);
}
} // 插入
node* insert(node *pnode, int key)
{ // 为空,则创建结点
if (NULL == pnode)
{
pnode = new(std::nothrow) node;
if (NULL != pnode)
{
pnode->data = key; pnode->height = 1;
return pnode;
}
} // 1、插入值比当前结点的值还要小,应该继续找左子树
if (key < pnode->data)
{
pnode->lc = insert(pnode->lc, key);
}
// 2、比当前值大。应该继续找当前结点的右子树
else if (key > pnode->data)
{
pnode->rc = insert(pnode->rc, key);
}
else
{
// 3、相等,树中已经存在插入的结点了,无法继续插入相同结点
return pnode;
} // 4、更新结点的高度
pnode->height = max(height(pnode->lc), height(pnode->rc)) + 1; // 5、获取插入后的平衡情况的平衡因子
int pnode_bf = get_balance_factor(pnode); // 6、下面开始判断新插入节点的平衡因子,当前插入结果是否需要旋转结点
// 6.1 若为LL型,右旋
if ( (1 < pnode_bf) && (key < pnode->data) )
return rotate_ll(pnode);
// 6.2 若为RR型,左旋
if ( (-1 > pnode_bf) && (key > pnode->data) )
return rotate_rr(pnode);
// 6.3 若为LR型
if ( (1 < pnode_bf) && (key > pnode->data) )
return rotate_lr(pnode);
// 6.4 若为RL型
if ( (-1 > pnode_bf) && (key < pnode->data) )
return rotate_rl(pnode); return pnode;
} // 找到参数结点的父节点
node *get_parent_node(node *pnode)
{
if (NULL == pnode)
{
return pnode;
} node *parent_node = NULL;
int key = pnode->data; node *cur_node = root;
while (cur_node)
{
if (key == cur_node->data)
break;
else if (key < cur_node->data)
{
parent_node = cur_node;
cur_node = cur_node->lc;
}
else if (key > cur_node->data)
{
parent_node = cur_node;
cur_node = cur_node->rc;
}
} return parent_node;
} // 删除
node *del_node2(node *pnode, int key)
{
if (NULL == pnode)
{
return pnode;
} // 找 删除结点的位置
if (key < pnode->data)
{
pnode->lc = del_node2(pnode->lc, key);
}
else if (key > pnode->data)
{
pnode->rc = del_node2(pnode->rc, key);
}
else
{
// 找到删除结点, // 3.1 若删除结点同时存在左右孩子
if ( (NULL != pnode->lc) && (NULL != pnode->rc) )
{
// 需要从左右子树中最高的那棵树开始删除 // 从左子树中开始删除
if (height(pnode->lc) > height(pnode->rc))
{
// 找到左子树中最大的结点,替换当前结点
node *pre_node = get_max(pnode->lc); // 将当前结点的值替换为前驱结点的值
pnode->data = pre_node->data; // 删除左子树中最大的结点
pnode->lc = del_node2(pnode->lc, pnode->data);
} // 删除的结点从右子树中开始找
else
{
// 找到后继结点
node *successor_node = get_min(pnode->rc); // 将删除结点的值赋值给当前结点
pnode->data = successor_node->data; // 删除后继结点中指定结点的值
pnode->rc = del_node2(pnode->rc, pnode->data);
} } // 3.2 若只有左孩子
else if ((NULL != pnode->lc) && (NULL == pnode->rc) )
{
// 指向需要删除的结点
node *del_node_tmp = pnode->lc; // 将孩子的值赋值给删除结点
pnode->data = del_node_tmp->data;
// 将父节点指向孩子结点的分支设置为NULL
pnode->lc = NULL; delete del_node_tmp;
del_node_tmp = NULL;
}
// 3.3 若只有右孩子
else if ( (NULL == pnode->lc) && (NULL != pnode->rc))
{
// 指向需要删除的结点
node *del_node_tmp = pnode->rc;
pnode->data = del_node_tmp->data;
// 将分支设置为NULL
del_node_tmp->rc = NULL; delete del_node_tmp;
del_node_tmp = NULL;
}
// 3.4 删除的是叶子结点
else
{ node *pnode_pa = get_parent_node(pnode);
if (NULL != pnode_pa)
{
if (pnode == pnode_pa->lc)
pnode_pa->lc = NULL;
else if (pnode == pnode_pa->rc)
pnode_pa->rc = NULL;
}
// 说明只有根结点
else
root = NULL; delete pnode;
pnode = NULL;
}
} // 为了保证树的平衡
if (NULL == pnode)
return pnode; // ------- 删除节点后,需要做平衡
pnode->height = max(height(pnode->lc), height(pnode->rc)) + 1; // 当前结点的平衡因子
int pnode_bf = get_balance_factor(pnode); // LL型
if ( (1 < pnode_bf) && (0 <= get_balance_factor(pnode->lc)) )
{
return rotate_ll(pnode);
}
// LR型
if ( (1 < pnode_bf) && (0 > get_balance_factor(pnode->lc)) )
{
return rotate_lr(pnode);
}
// RR型
if ( (-1 > pnode_bf) && (0 >= get_balance_factor(pnode->rc)) )
{
return rotate_rr(pnode);
} // RL型
if ( (-1 > pnode_bf) && (0 < get_balance_factor(pnode->rc)) )
{
return rotate_rl(pnode);
} return pnode;
} private:
node *root;
}; int main()
{
avltree2 tree;
int insert_key = 0;
cout << "插入开始,结束插入 输入-1" << endl << endl << endl;
while (1)
{
cout << "请输入插入值:";
cin >> insert_key;
if (-1 == insert_key)
break;
tree.insert(insert_key);
cout << endl << "插入后,中序遍历结果:";
tree.in_order_traverse(); cout << endl << endl;
} cout << "前序遍历:";
tree.pre_order_traverse();
cout << endl; cout << "========================================" << endl;
cout << "开始删除, 请输入删除元素的值, 结束输入-1" << endl << endl;
int del_key = 0;
while (1)
{
cout << "请输入删除结点值: ";
cin >> del_key;
if (-1 == del_key)
break; tree.remove_node(del_key);
cout << "删除后,中序遍历:";
tree.in_order_traverse();
cout << endl << "删除后,前序遍历:";
tree.pre_order_traverse();
cout << endl << endl;
} cout << endl << endl << endl << "----------------------------" << endl << endl;
cout << "删除树" << endl;
tree.remove(); return 0;
}
平衡二叉树c++实现
2、结果:
3、GitHub 地址:https://github.com/mohistH/base_data_structure
平衡二叉树(c++)实现(存在问题:插入节点后,问题:调整树的结构存在问题)的更多相关文章
- Java实现二叉搜索树的添加,前序、后序、中序及层序遍历,求树的节点数,求树的最大值、最小值,查找等操作
什么也不说了,直接上代码. 首先是节点类,大家都懂得 /** * 二叉树的节点类 * * @author HeYufan * * @param <T> */ class Node<T ...
- Android零基础入门第15节:掌握Android Studio项目结构,扬帆起航
原文:Android零基础入门第15节:掌握Android Studio项目结构,扬帆起航 经过前面的学习,Android Studio开发环境已准备OK,运行Android应用程序的原生模拟器和Ge ...
- ztree删除某个节点下的全部子节点后,父节点图标还是文件夹
<script type="text/javascript"> //删除节点 zTree.removeNode(treeNode); //获取删除节点的父节点 var ...
- 使用zTree展开节点后,覆盖了下一个节点
如图所示,结果是zTree与<fieldset>标签不兼容....我去!!! 也就是说Ztree不能放在<fieldset>标签中..
- 第二节 java流程控制(循环结构)
1.for循环 for(初始化表达式;循环条件表达式;循环后的操作表达式){ 执行语句 } 2.while循环 while(条件表达式){ 执行语句 } while循环特点是只有条件满足才会执行我们 ...
- 第二节 java流程控制(判断结构+选择结构)
Java的判断结构: 1.if(条件表达式){ 执行语句 }: 2.if(条件表达式){ 执行语句 }else{ 执行语句 } 3. if(条件表达式){ 执行语句 }else if(条件表达式){ ...
- 学习JDK1.8集合源码之--TreeMap
1. TreeMap简介 TreeMap继承自AbstractMap,实现了NavigableMap.Cloneable.java.io.Serializable接口.所以TreeMap也是一个key ...
- 平衡二叉树(Balanced Binary Tree 或 Height-Balanced Tree)又称AVL树
平衡二叉树(Balanced Binary Tree 或 Height-Balanced Tree)又称AVL树 (a)和(b)都是排序二叉树,但是查找(b)的93节点就需要查找6次,查找(a)的93 ...
- 红黑树与AVL(平衡二叉树)的区别
关于红黑树和AVL树,来自网络: 1 好处 及 用途 红黑树 并不追求“完全平衡 ”——它只要求部分地达到平衡要求,降低了对旋转的要求,从而提高了性能. 红黑树能够以 O(log2 n) 的时间复 ...
随机推荐
- Qtree V
lmn u 表示 u 所在splay子树最上方点距离最近的白点 rmn u 表示 u 所在splay子树最下方点距离最近的白点 开一个set维护所有虚儿子能走到的最近的白点的距离 考虑pushup, ...
- 什么是总线、总线的类型、局部总线、局部总线类型和什么是接口方式?什么是IDE?什么是SCSI?
在安装电脑系统时,进行内核配置时涉及到各种总线类型,有必要了解一下什么是总线.总线的类型.局部总线.局部总线类型和接口方式. 1)总线总线是一组通信线.在机器内部, 各部件通过总线连接; 对于外部设备 ...
- Golang使用validator进行数据校验及自定义翻译器
Golang使用validator进行数据校验及自定义翻译器 包下载:go get github.com/go-playground/validator/v10 一.概述 在接口开发经常会遇到一个问题 ...
- EPOLL原理详解(图文并茂)
文章核心思想是: 要清晰明白EPOLL为什么性能好. 本文会从网卡接收数据的流程讲起,串联起CPU中断.操作系统进程调度等知识:再一步步分析阻塞接收数据.select到epoll的进化过程:最后探究e ...
- Python | 迭代器与zip的一些细节
首先抛出一个困扰本人许久的问题: nums = [1,2,3,4,5,6] numsIter = iter(nums) for _ in zip(*[numsIter]*3): print(_) pr ...
- 7 — 简单了解springboot中的thymeleaf
1.官网学习地址 https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 2.什么是thymeleaf? 一张图看明白: 解读: ...
- 【讨论】APP的免填邀请码解决方案
00x0 具体需求 app中已注册的用户分享一个含有邀请码的二维码,分享到朋友圈新用户在朋友圈打开这个这个链接下载app.新用户安装后打开app后就自动绑定邀请码要求用户不填写任何东西 朋友老板出差给 ...
- Angular Service设计理念及使用
官方认为组件不应该直接获取或保存数据, 它们应该聚焦于展示数据,而把数据访问的职责委托给某个服务. 而服务就充当着数据访问,逻辑处理的功能.把组件和服务区分开,以提高模块性和复用性. 1.依赖注入 注 ...
- SpringIOC原理浅析
1. IoC理论的背景我们都知道,在采用面向对象方法设计的软件系统中,它的底层实现都是由N个对象组成的,所有的对象通过彼此的合作,最终实现系统的业务逻辑. 图1:软件系统中耦合的对象 如果我们打开机械 ...
- vueAPI (data,props,methods,watch,computed,template,render)
data Vue 实例的数据对象.Vue 将会递归将 data 的属性转换为 getter/setter,从而让 data 的属性能够响应数据变化.实例创建之后,可以通过vm.$data来访问原始数据 ...