AVL平衡二叉查找树
二叉排序树:
定义
二叉排序树,又叫二叉查找树,它或者是一棵空树;或者是具有以下性质的二叉树:
1. 若它的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
2. 若它的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
3. 它的左右子树也分别为二叉排序树。
比如下图就是一棵普通的二叉排序树:
如果按照中序遍历的顺序,一棵二叉排序树的输出结果就刚好是按照从小到大的顺序输出的,可以运用于二分算法。
先对其数据结构进行定义:
typedef struct Binary_Tree_node
{
int data; //数据域
struct Binary_Tree_node* lchild, * rchild; //左右孩子结点
}Binode, * BiTree;
然后是插入操作:
//假设没有相等的data,这里我们不考虑相等的数据
//插入结点
void Insert_Binary_Tree(BiTree& bst ,int t) //bst是根节点
{
if (bst == NULL) //空树或者递归到了叶结点
{
BiTree newp = new Binode;
newp->data = t;
newp->lchild = NULL;
newp->rchild = NULL;
bst = newp;
}
else
{
if (t > bst->data) //比本结点大,则插入其右孩子结点,小就插入左孩子结点
Insert_Binary_Tree(bst->rchild, t);
else
Insert_Binary_Tree(bst->lchild, t);
}
}
创建一棵树:
//创建一棵二叉排序树
BiTree Create_Binary_Tree()
{
BiTree bst = NULL;
int t;
cin >> t;
while (t != ENDKEY) //只要输入不等于-1,就一直往树里插入元素
{
Insert_Binary_Tree(bst, t);
cin >> t;
}
return bst;
}
删除操作:删除操作比较复杂,本篇博客主要是记录AVL,所以此处不做赘述
BiTree Delete_Binary_Tree(BiTree bst, int t)
{
Binode* newp, * f, * s, * q;
newp = bst;
f = NULL;
while (newp)
{
if (newp->data == t)
break;
f = newp;
if (t > newp->data)
newp = newp->rchild;
else
newp = newp->lchild;
}
if (newp == NULL)
return bst;
if (newp->lchild == NULL)
{
if (f == NULL)
bst = bst->rchild;
else if (f->lchild == newp)
f->lchild = newp->rchild;
else
f->rchild = newp->rchild;
delete[]newp;
}
else if (newp->rchild == NULL)
{
if (f == NULL)
bst = bst->lchild;
else if (f->lchild == newp)
f->lchild = newp->lchild;
else
f->rchild = newp->lchild;
delete[]newp;
}
else
{
q = newp;
s = newp->lchild;
while (s->rchild)
{
q = s;
s = s->rchild;
}
if (q == newp)
q->lchild = s->lchild;
else
q->rchild = s->lchild;
newp->data = s->data;
delete[]s;
}
return bst;
}
搜索二叉树:
//搜索二叉树,根据输入的数据搜索二叉树,返回这个数据的结点
BiTree Search_Binary_Tree(BiTree bst, int t)
{
if (bst == NULL)
return NULL;
if (bst->data == t)
return bst;
else if (t > bst->data)
return Search_Binary_Tree(bst->rchild, t);
else
return Search_Binary_Tree(bst->lchild, t);
}
平衡二叉排序树:
可是当一棵二叉排序树的某个节点的一枝相比于另一枝太长,搜索的时间复杂度就会变成O(n),而为了提高效率,提出了AVL树,即平衡二叉排序树。 例如下图就是一棵不平衡的二叉树:
像这样如果要搜索0元素就会变得和线性表的时间复杂度一样。 AVL树是一种比查找二叉树还特别的树,这种树就可以帮助我们解决二叉查找树刚才的那种所有节点都倾向一边的缺点的。具有如下特性:
1、具有二叉查找树的全部特性。
2、每个节点的左子树和右子树的高度差至多等于1。
例如:图一就是一颗AVL树了,而图二则不是(节点右边标的是这个节点的高度)。
按照这种规则进行建立二叉树,可以保证这棵二叉树始终是左右相对平衡的,可以保证搜索的时间复杂度达到O(log n),提高了效率。
针对树的失衡有四种情况,我们总结如下:
1、左左型:做右旋
2、右右型:做左旋
3、左右型:先左旋,再右旋
4、右左型:先右旋,再左旋
下面一个一个写,对于实现每个旋转的函数可以作为私有的对内接口(C++),不对外开放使用
1、左左型:做右旋:
像下面这种:
我们做如下右旋操作:
即顺时针旋转结点,使双亲结点被自己的左孩子替代,然后自己变成左孩子结点的右孩子结点
代码实现:
//左左型单旋转
void Lleft(BiTree &bst) //bst为不平衡的结点
{
BiTree lch = bst->lchild; //保存不平衡结点的左孩子结点
bst->lchild = lch->rchild;
lch->rchild = bst;
bst = lch;
}
2、右右型:做左旋:
像下面这种:
逆时针旋转结点,使自己被自己的右孩子替代,然后自己变成右孩子的左孩子结点
代码实现:
//右右型单旋转
void Rright(BiTree &bst) //bst为不平衡的结点
{
BiTree rch = bst->rchild; //保存不平衡结点的右孩子结点
bst->rchild = rch->lchild;
rch->lchild = bst;
bst = rch;
}
3、左右型:先左旋,再右旋:
像下面这种:
先对其左旋将其变成左左型,再右旋让其平衡
代码实现:
//左右型双旋转
void Lright(BiTree &bst)
{
//先做左旋,将其变成左左型
BiTree lch = bst->lchild;
BiTree lrch = bst->lchild->rchild;
bst->lchild = lrch;
bst->lchild->lchild = lch;
bst->lchild->lchild->rchild = NULL; //可能存在bug todo,目前来看没有哈哈哈
Lleft(bst); //再将其右旋
}
4、右左型:先右旋,再左旋:
老规矩,先贴图:
代码实现:
//右左型双旋转
void Rleft(BiTree &bst)
{
//先做右旋,将其变成右右型
BiTree rch = bst->rchild;
BiTree rlch = bst->rchild->lchild;
bst->rchild = rlch;
bst->rchild->rchild = rch;
bst->rchild->rchild->lchild = NULL;
Rright(bst); //再右旋
}
实现重点来了
我们每插入一个结点元素都要保证整棵树的每一个结点是平衡的,就要在插入这个结点后,从这个插入的结点往上逐个双亲结点去检查,看是否破坏了树的平衡,如果破坏了则对其进行左右旋转操作,保证其平衡性,直到树的根节点。
因为树的递归性,在插入的时候是递归插入,
按照栈的顺序,递归的过程中第一次递归的函数先入栈,以此类推,然后直到到达结束条件,前面的才依次出栈,
所以可以在插入函数最后面使用检查每个结点的函数,出栈过程中,就可以沿着新插入结点一路回退检查每个双亲结点了。
注意最后一行
代码实现:
//插入结点
void Insert_Binary_Tree(BiTree& bst ,int t) //bst是根节点
{
if (bst == NULL) //空树或者叶结点
{
BiTree newp = new Binode;
newp->data = t;
newp->lchild = NULL;
newp->rchild = NULL;
bst = newp;
}
else
{
if (t > bst->data)
Insert_Binary_Tree(bst->rchild, t);
else
Insert_Binary_Tree(bst->lchild, t);
}
//在最后检查这个结点,在递归的时候就可以将递归过程中从根节点往下的所有结点都检查了,按照出栈顺序,可以保证都是在插入后才依次向上检查的。
Check_Binary_Tree(bst);
}
要实现这一功能就必须有一个检查是否平衡的依据———就是每个结点的左右子树的深度。这个函数是作为私有的对内接口。
计算深度的实现:
//一个结点的深度
int Binary_Tree_height(BiTree bst) //bst为要计算的结点
{
if (bst == NULL)
return 0;
int l = Binary_Tree_height(bst->lchild);
int r = Binary_Tree_height(bst->rchild);
return max(l, r) + 1;
}
然后是一个检查这个结点是否平衡的函数,对外:
检查平衡函数:
//如果出现不平衡的情况就旋转
void Check_Binary_Tree(BiTree &bst) //bst为要检查的结点
{
if (bst == NULL)
return;
if (Binary_Tree_height(bst->lchild) - Binary_Tree_height(bst->rchild) > 1)
{
if (Binary_Tree_height(bst->lchild->lchild) > Binary_Tree_height(bst->lchild->rchild))
Lleft(bst);
else
Lright(bst);
}
if (Binary_Tree_height(bst->rchild) - Binary_Tree_height(bst->lchild) > 1)
{
if (Binary_Tree_height(bst->rchild->rchild) > Binary_Tree_height(bst->rchild->lchild))
Rright(bst);
else
Rleft(bst);
}
}
最后,对代码进行一下汇总:
AVL.h文件
#pragma once
#include<iostream>
constexpr auto ENDKEY = -1;
template<typename T1, typename T2>
constexpr auto max(T1 x, T2 y) { return x>y?x:y; }
using namespace std;
typedef struct Binary_Tree_node
{
int data; //数据域
struct Binary_Tree_node* lchild, * rchild; //左右孩子结点
}Binode, * BiTree;
//访问二叉树
void visit(int c, int level)
{
printf("%d位于第%d层\n", c, level);
}
//中序遍历
void Midorder_Traverse(BiTree T, int level)
{
if (T)
{
Midorder_Traverse(T->lchild, level + 1);
visit(T->data, level);
Midorder_Traverse(T->rchild, level + 1);
}
}
//一个结点的深度
int Binary_Tree_height(BiTree bst)
{
if (bst == NULL)
return 0;
int l = Binary_Tree_height(bst->lchild);
int r = Binary_Tree_height(bst->rchild);
return max(l, r) + 1;
}
//遍历整棵树,如果出现不平衡的情况就旋转
//左左型单旋转
void Lleft(BiTree &bst)
{
BiTree lch = bst->lchild; //保存不平衡结点的左孩子结点
bst->lchild = lch->rchild;
lch->rchild = bst;
bst = lch;
}
//右右型单旋转
void Rright(BiTree &bst)
{
BiTree rch = bst->rchild; //保存不平衡结点的右孩子结点
bst->rchild = rch->lchild;
rch->lchild = bst;
bst = rch;
}
//左右型双旋转
void Lright(BiTree &bst)
{
//先做左旋,将其变成左左型
BiTree lch = bst->lchild;
BiTree lrch = bst->lchild->rchild;
bst->lchild = lrch;
bst->lchild->lchild = lch;
bst->lchild->lchild->rchild = NULL; //可能存在bug todo
Lleft(bst);
}
//右左型双旋转
void Rleft(BiTree &bst)
{
//先做右旋,将其变成右右型
BiTree rch = bst->rchild;
BiTree rlch = bst->rchild->lchild;
bst->rchild = rlch;
bst->rchild->rchild = rch;
bst->rchild->rchild->lchild = NULL;
Rright(bst);
}
void Check_Binary_Tree(BiTree &bst)
{
if (bst == NULL)
return;
if (Binary_Tree_height(bst->lchild) - Binary_Tree_height(bst->rchild) > 1)
{
if (Binary_Tree_height(bst->lchild->lchild) > Binary_Tree_height(bst->lchild->rchild))
Lleft(bst);
else
Lright(bst);
}
if (Binary_Tree_height(bst->rchild) - Binary_Tree_height(bst->lchild) > 1)
{
if (Binary_Tree_height(bst->rchild->rchild) > Binary_Tree_height(bst->rchild->lchild))
Rright(bst);
else
Rleft(bst);
}
//Check_Binary_Tree(bst->lchild);
//Check_Binary_Tree(bst->rchild);
}
//假设没有相等的data
//插入结点
void Insert_Binary_Tree(BiTree& bst ,int t) //bst是根节点
{
if (bst == NULL) //空树或者叶结点
{
BiTree newp = new Binode;
newp->data = t;
newp->lchild = NULL;
newp->rchild = NULL;
bst = newp;
}
else
{
if (t > bst->data)
Insert_Binary_Tree(bst->rchild, t);
else
Insert_Binary_Tree(bst->lchild, t);
}
Check_Binary_Tree(bst);
}
//创建一棵二叉排序树
BiTree Create_Binary_Tree()
{
BiTree bst = NULL;
int t;
cin >> t;
while (t != ENDKEY)
{
Insert_Binary_Tree(bst, t);
cin >> t;
}
return bst;
}
//二叉排序树的删除
BiTree Delete_Binary_Tree(BiTree bst, int t)
{
Binode* newp, * f, * s, * q;
newp = bst;
f = NULL;
while (newp)
{
if (newp->data == t)
break;
f = newp;
if (t > newp->data)
newp = newp->rchild;
else
newp = newp->lchild;
}
if (newp == NULL)
return bst;
if (newp->lchild == NULL)
{
if (f == NULL)
bst = bst->rchild;
else if (f->lchild == newp)
f->lchild = newp->rchild;
else
f->rchild = newp->rchild;
delete[]newp;
}
else if (newp->rchild == NULL)
{
if (f == NULL)
bst = bst->lchild;
else if (f->lchild == newp)
f->lchild = newp->lchild;
else
f->rchild = newp->lchild;
delete[]newp;
}
else
{
q = newp;
s = newp->lchild;
while (s->rchild)
{
q = s;
s = s->rchild;
}
if (q == newp)
q->lchild = s->lchild;
else
q->rchild = s->lchild;
newp->data = s->data;
delete[]s;
}
Check_Binary_Tree(bst);
return bst;
}
//搜索二叉树
BiTree Search_Binary_Tree(BiTree bst, int t)
{
if (bst == NULL)
return NULL;
if (bst->data == t)
return bst;
else if (t > bst->data)
return Search_Binary_Tree(bst->rchild, t);
else
return Search_Binary_Tree(bst->lchild, t);
}
测试数据
#include<iostream>
#include"BST.h"
using namespace std;
int main()
{
BiTree bst = Create_Binary_Tree();
int level = 1;
Midorder_Traverse(bst, level);
system("pause");
}
测试结果:
AVL平衡二叉查找树的更多相关文章
- 平衡树初阶——AVL平衡二叉查找树+三大平衡树(Treap + Splay + SBT)模板【超详解】
平衡树初阶——AVL平衡二叉查找树 一.什么是二叉树 1. 什么是树. 计算机科学里面的树本质是一个树状图.树首先是一个有向无环图,由根节点指向子结点.但是不严格的说,我们也研究无向树.所谓无向树就是 ...
- AVL树(平衡二叉查找树)
首先要说AVL树,我们就必须先说二叉查找树,先介绍二叉查找树的一些特性,然后我们再来说平衡树的一些特性,结合这些特性,然后来介绍AVL树. 一.二叉查找树 1.二叉树查找树的相关特征定义 二叉树查找树 ...
- 【查找结构3】平衡二叉查找树 [AVL]
在上一个专题中,我们在谈论二叉查找树的效率的时候.不同结构的二叉查找树,查找效率有很大的不同(单支树结构的查找效率退化成了顺序查找).如何解决这个问题呢?关键在于如何最大限度的减小树的深度.正是基于这 ...
- 数据结构-自平衡二叉查找树(AVL)详解
介绍: 在计算机科学中,AVL树是最先发明的自平衡二叉查找树. 在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树. 查找.插入和删除在平均和最坏情况下都是O(log n).增 ...
- 算法学习 - 平衡二叉查找树实现(AVL树)
平衡二叉查找树 平衡二叉查找树是非常早出现的平衡树,由于全部子树的高度差不超过1,所以操作平均为O(logN). 平衡二叉查找树和BS树非常像,插入和删除操作也基本一样.可是每一个节点多了一个高度的信 ...
- 平衡二叉查找树 AVL 的实现
不同结构的二叉查找树,查找效率有很大的不同(单支树结构的查找效率退化成了顺序查找).如何解决这个问题呢?关键在于如何最大限度的减小树的深度.正是基于这个想法,平衡二叉树出现了. 平衡二叉树的定义 (A ...
- 006-数据结构-树形结构-二叉树、二叉查找树、平衡二叉查找树-AVL树
一.概述 树其实就是不包含回路的连通无向图.树其实是范畴更广的图的特例. 树是一种数据结构,它是由n(n>=1)个有限节点组成一个具有层次关系的集合. 1.1.树的特性: 每个结点有零个或多个子 ...
- 二叉搜索树、AVL平衡二叉搜索树、红黑树、多路查找树
1.二叉搜索树 1.1定义 是一棵二叉树,每个节点一定大于等于其左子树中每一个节点,小于等于其右子树每一个节点 1.2插入节点 从根节点开始向下找到合适的位置插入成为叶子结点即可:在向下遍历时,如果要 ...
- 二叉查找树(二叉排序树)的详细实现,以及随机平衡二叉查找树Treap的分析与应用
这是一篇两年前写的东西,自我感觉还是相当不错的Treap教程.正好期末信息科学技术概论课要求交一个论文,就把这个东西修改了一下交了,顺便也发到这里吧. 随机平衡二叉查找树Treap的分析与应用 1.序 ...
随机推荐
- 【Offer】[5] 【替换空格】
题目描述 思路分析 Java代码 代码链接 题目描述 请实现一个函数,把字符串中的每个空格替换成"%20". 例如输入"We are happy.",则输出&q ...
- 从壹开始学习NetCore 44 ║ 最全的 netcore 3.0 升级实战方案
缘起 哈喽大家中秋节(后)好呀!感觉已经好久没有写文章了,但是也没有偷懒哟,我的视频教程<系列一.NetCore 视频教程(Blog.Core)>也已经录制八期了,还在每周末同步更新中,欢 ...
- Netty源码分析 (八)----- write过程 源码分析
上一篇文章主要讲了netty的read过程,本文主要分析一下write和writeAndFlush. 主要内容 本文分以下几个部分阐述一个java对象最后是如何转变成字节流,写到socket缓冲区中去 ...
- HBase读延迟的12种优化套
任何系统都会有各种各样的问题,有些是系统本身设计问题,有些却是使用姿势问题.HBase也一样,在真实生产线上大家或多或少都会遇到很多问题,有些是HBase还需要完善的,有些是我们确实对它了解太少. 总 ...
- 微信小程序商城构建全栈应用 Thinkphp5
课程——微信小程序商城构建全栈应用[目录]第1章 前言:不同的时代,不同的Web第2章 环境,工具与准备工作第3章 模块,路由与获取请求参数第4章 构建验证层第5章 REST与RESTFul第6章 A ...
- python pandas进行条件筛选时出现ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().”
在使用pandas进行条件筛选时,使用了如下的代码: fzd_index=data[(data['实际辐照度']<mi)or(data['实际辐照度']>ma)].index 原本以为,并 ...
- Tomcat乱码
日志乱码修改logging.properties文件中encoding=UTF-8 vim logging.properties encoding=utf-8 wq!保持退出,重启tomcat 乱码O ...
- 【数据结构】Hash表
[数据结构]Hash表 Hash表也叫散列表,是一种线性数据结构.在一般情况下,可以用o(1)的时间复杂度进行数据的增删改查.在Java开发语言中,HashMap的底层就是一个散列表. 1. 什么是H ...
- 47 (OC)* OC反射机制
Objective-C反射机制:是类似于Java的反射机制,这种动态机制可以让oc语言更加的灵活.这句话是对oc反射机制的初步认识,不过具体的怎么类似于java机制,怎么让oc更加的灵活,还得看下面的 ...
- SQL Server 内存优化表的索引设计
测试的版本:SQL Server 2017 内存优化表上可以创建哈希索引(Hash Index)和内存优化非聚集(NONCLUSTERED)索引,这两种类型的索引也是内存优化的,称作内存优化索引,和基 ...