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

作者: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. VS调用别人的COM组件的问题

    调用第三方的COM组件,记得要先在管理员cmd执行:regsvr32 xxxx.dll 没执行之前运行 HRESULT hr = pComm.CreateInstance("xxxx.Com ...

  2. Linux之crond任务调度

    1. 示意图 2. 基本语法 crontab [选项] # -e : 编辑crontab定时任务 # -l : 查询crontab # -r : 删除当前用户所有的crontab任务 # 例子: # ...

  3. Linux—查看内核版本、系统版本、系统位数

    一.查看内核版本命令: 1) [root@q1test01 ~]# cat /proc/version   Linux version 2.6.9-22.ELsmp (bhcompile@crowe. ...

  4. Redis集合解决大数据筛选

    Redis集合:集合是什么,就是一堆确定的数据放在一起,数学上集合有交集.并集的概念,这个就可以用来做大数据的筛选功能. 以商品为例,假如商品有颜色和分类.价格区间等属性. 给所有统一颜色的商品放一个 ...

  5. sig mesh 培训-18304

    1.mesh 的传输速率 ---有效数据最长的长度是10个字节 ---最小时间间隔是10ms,重传1次 --建议数据包之间不少于100ms 1S = 10*10 =100个字节 2.目前telink ...

  6. Redis | 第9章 Lua 脚本与排序《Redis设计与实现》

    目录 前言 1. Lua 脚本 1.1 Redis 创建并修改 Lua 环境的步骤 1.2 Lua 环境协作组件 1.3 EVAL 命令的实现 1.4 EVALSHA 命令的实现 1.5 脚本管理命令 ...

  7. Spark Stage 的划分

    Spark作业调度 对RDD的操作分为transformation和action两类,真正的作业提交运行发生在action之后,调用action之后会将对原始输入数据的所有transformation ...

  8. 【Reverse】DLL注入

    DLL注入就是将dll粘贴到指定的进程空间中,通过dll状态触发目标事件 DLL注入的大概流程 https://uploader.shimo.im/f/CXFwwkEH6FPM0rtT.png!thu ...

  9. 转 Android中Activity的启动模式(LaunchMode)和使用场景

    转载请注明出处:http://blog.csdn.net/sinat_14849739/article/details/78072401本文出自Shawpoo的专栏我的简书:简书 一.为什么需要启动模 ...

  10. ORACLE 获取执行计划的方法

    一.获取执行计划的6种方法(详细步骤已经在每个例子的开头注释部分说明了): 1. explain plan for获取: 2. set autotrace on : 3. statistics_lev ...