平衡二叉树(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) 的时间复 ...
随机推荐
- Atcoder Regular Contest 123 题解
u1s1 我是真的不知道为什么现场这么多人切了 D,感觉 D 对思维要求显然要高于其他 300+ 人切掉的 D 吧(也有可能是 Atc 用户整体水平提升了?) A 开 幕 雷 击(这题似乎 wjz 交 ...
- CSP-S 2021 游记
福兮祸之所伏 胜利是一种肯定,代表我应该在这条路上坚定不移地走下去. 胜利也是一种危机,它粉饰太平.养虎自齧,并把人最丑陋的一些想法暴露出来:虚荣心.骄傲心都在这个过程中被放大,懒惰心.自满心也找到了 ...
- python14异常处理
def test_div(num1,num2): return num1 / num2 if __name__ == "__main__": try: print(test_div ...
- java面试题目偏基础
一.JAVA基础篇-概念1.简述你所知道的Linux:Linux起源于1991年,1995年流行起来的免费操作系统,目前, Linux是主流的服务器操作系统, 广泛应用于互联网.云计算.智能手机(An ...
- 『与善仁』Appium基础 — 19、元素定位工具(三)
目录 1.Chrome Inspect介绍 2.Chrome Inspect打开方式 3.Chrome Inspect工具的使用 (1)Chrome Inspect工作前提 (2)Chrome Ins ...
- 解决CentOS7 docker容器映射端口只监听ipv6的问题
问题现象 docker容器起来以后,查看9100端口监听情况,如下图: $ ss -lntp State Recv-Q Send-Q Local Address:Port Peer Address:P ...
- 巩固javaweb第十天
巩固内容: HTML <meta> 元素 meta标签描述了一些基本的元数据. <meta> 标签提供了元数据.元数据也不显示在页面上,但会被浏览器解析. META 元素通常用 ...
- MyBatis 如何实现流式查询
基本概念 流式查询指的是查询成功后不是返回一个集合而是返回一个迭代器,应用每次从迭代器取一条查询结果.流式查询的好处是能够降低内存使用. 如果没有流式查询,我们想要从数据库取 1000 万条记录而又没 ...
- oracle 根据ids转names
WITH t AS (SELECT '1,2,3,4' a, 1 b FROM Dual UNION ALL SELECT '1,2,3' a, 2 b FROM Dual),p AS ( ...
- java实现链式线性表
package ch9; public class LinkList <T>{ private class Node { //保存节点的数据 private T data; //指向下一个 ...