!!版权声明:本文为博主原创文章,版权归原文作者和博客园共有,谢绝任何形式的 转载!!

作者: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++)实现(存在问题:插入节点后,问题:调整树的结构存在问题)的更多相关文章

  1. Java实现二叉搜索树的添加,前序、后序、中序及层序遍历,求树的节点数,求树的最大值、最小值,查找等操作

    什么也不说了,直接上代码. 首先是节点类,大家都懂得 /** * 二叉树的节点类 * * @author HeYufan * * @param <T> */ class Node<T ...

  2. Android零基础入门第15节:掌握Android Studio项目结构,扬帆起航

    原文:Android零基础入门第15节:掌握Android Studio项目结构,扬帆起航 经过前面的学习,Android Studio开发环境已准备OK,运行Android应用程序的原生模拟器和Ge ...

  3. ztree删除某个节点下的全部子节点后,父节点图标还是文件夹

    <script type="text/javascript"> //删除节点 zTree.removeNode(treeNode); //获取删除节点的父节点 var ...

  4. 使用zTree展开节点后,覆盖了下一个节点

    如图所示,结果是zTree与<fieldset>标签不兼容....我去!!! 也就是说Ztree不能放在<fieldset>标签中..

  5. 第二节 java流程控制(循环结构)

     1.for循环 for(初始化表达式;循环条件表达式;循环后的操作表达式){ 执行语句 } 2.while循环 while(条件表达式){ 执行语句 } while循环特点是只有条件满足才会执行我们 ...

  6. 第二节 java流程控制(判断结构+选择结构)

    Java的判断结构: 1.if(条件表达式){ 执行语句 }: 2.if(条件表达式){ 执行语句 }else{ 执行语句 } 3. if(条件表达式){ 执行语句 }else if(条件表达式){ ...

  7. 学习JDK1.8集合源码之--TreeMap

    1. TreeMap简介 TreeMap继承自AbstractMap,实现了NavigableMap.Cloneable.java.io.Serializable接口.所以TreeMap也是一个key ...

  8. 平衡二叉树(Balanced Binary Tree 或 Height-Balanced Tree)又称AVL树

    平衡二叉树(Balanced Binary Tree 或 Height-Balanced Tree)又称AVL树 (a)和(b)都是排序二叉树,但是查找(b)的93节点就需要查找6次,查找(a)的93 ...

  9. 红黑树与AVL(平衡二叉树)的区别

    关于红黑树和AVL树,来自网络: 1 好处 及 用途 红黑树 并不追求“完全平衡 ”——它只要求部分地达到平衡要求,降低了对旋转的要求,从而提高了性能. 红黑树能够以 O(log2  n)  的时间复 ...

随机推荐

  1. Anaconda建立新的环境,出现CondaHTTPError: HTTP 000 CONNECTION FAILED for url ...... 解决过程

    2020.3.7准备scrapy,使用anaconda创建一个新的环境,执行"conda create -n scrapyEnv python=3.6",结果出现了"Co ...

  2. 【机器学习与R语言】6-线性回归

    目录 1.理解回归 1)简单线性回归 2)普通最小二乘估计 3)相关系数 4)多元线性回归 2.线性回归应用示例 1)收集数据 2)探索和准备数据 3)训练数据 4)评估模型 5)提高模型性能 1.理 ...

  3. Shell 格式化输出printf、awk

    目录 Shell 文件的格式化与相关处理 printf.awk 格式化打印printf 案例.格式化输出文件内容 输出命令echo 案例 awk数据处理工具 语法格式 处理流程 AWK内置变量 条件 ...

  4. 面向多场景而设计的 Erda Pipeline

    作者|林俊(万念) 来源|尔达 Erda 公众号 Erda Pipeline 是端点自研.用 Go 编写的一款企业级流水线服务.截至目前,已经为众多行业头部客户提供交付和稳定的服务. 为什么我们坚持自 ...

  5. Oracle—表、约束、索引、表空间、分区、序列、统计信息

    表.约束.索引.表空间.分区.序列.统计信息 一.表及其操作 1.创建表 create table 表名 ( 字段名1 字段类型 默认值 是否为空 , 字段名2 字段类型 默认值 是否为空, 字段名3 ...

  6. Swift alert 倒计时

    let title: String = "您的开奖时间为" let time: String = "2017-10-23 12:23:18" let count ...

  7. 通过Shell统计PV和UV

    PV.UV是网站分析中最基础.最常见的指标.PV即PageView,网站浏览量,指页面的浏览次数,用以衡量网站用户访问的网页数量.用户没打开一个页面便记录1次PV,多次打开同一页面则浏览量累计:UV即 ...

  8. 【Java 多线程】Java线程池类ThreadPoolExecutor、ScheduledThreadPoolExecutor及Executors工厂类

    Java中的线程池类有两个,分别是:ThreadPoolExecutor和ScheduledThreadPoolExecutor,这两个类都继承自ExecutorService.利用这两个类,可以创建 ...

  9. 【Python】matplotlib直方图纵轴显示百分比

    其实很简单,就是算了一下百分比权重,乘以了一个权重值 import matplotlib.pyplot as plt from matplotlib.ticker import FuncFormatt ...

  10. 如何查看电脑IP地址

    如何查看电脑的IP地址 win+r输入cmd回车,然后输入:ipconfig回车