二叉排序树:

定义

        二叉排序树,又叫二叉查找树,它或者是一棵空树;或者是具有以下性质的二叉树:
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平衡二叉查找树的更多相关文章

  1. 平衡树初阶——AVL平衡二叉查找树+三大平衡树(Treap + Splay + SBT)模板【超详解】

    平衡树初阶——AVL平衡二叉查找树 一.什么是二叉树 1. 什么是树. 计算机科学里面的树本质是一个树状图.树首先是一个有向无环图,由根节点指向子结点.但是不严格的说,我们也研究无向树.所谓无向树就是 ...

  2. AVL树(平衡二叉查找树)

    首先要说AVL树,我们就必须先说二叉查找树,先介绍二叉查找树的一些特性,然后我们再来说平衡树的一些特性,结合这些特性,然后来介绍AVL树. 一.二叉查找树 1.二叉树查找树的相关特征定义 二叉树查找树 ...

  3. 【查找结构3】平衡二叉查找树 [AVL]

    在上一个专题中,我们在谈论二叉查找树的效率的时候.不同结构的二叉查找树,查找效率有很大的不同(单支树结构的查找效率退化成了顺序查找).如何解决这个问题呢?关键在于如何最大限度的减小树的深度.正是基于这 ...

  4. 数据结构-自平衡二叉查找树(AVL)详解

    介绍: 在计算机科学中,AVL树是最先发明的自平衡二叉查找树. 在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树. 查找.插入和删除在平均和最坏情况下都是O(log n).增 ...

  5. 算法学习 - 平衡二叉查找树实现(AVL树)

    平衡二叉查找树 平衡二叉查找树是非常早出现的平衡树,由于全部子树的高度差不超过1,所以操作平均为O(logN). 平衡二叉查找树和BS树非常像,插入和删除操作也基本一样.可是每一个节点多了一个高度的信 ...

  6. 平衡二叉查找树 AVL 的实现

    不同结构的二叉查找树,查找效率有很大的不同(单支树结构的查找效率退化成了顺序查找).如何解决这个问题呢?关键在于如何最大限度的减小树的深度.正是基于这个想法,平衡二叉树出现了. 平衡二叉树的定义 (A ...

  7. 006-数据结构-树形结构-二叉树、二叉查找树、平衡二叉查找树-AVL树

    一.概述 树其实就是不包含回路的连通无向图.树其实是范畴更广的图的特例. 树是一种数据结构,它是由n(n>=1)个有限节点组成一个具有层次关系的集合. 1.1.树的特性: 每个结点有零个或多个子 ...

  8. 二叉搜索树、AVL平衡二叉搜索树、红黑树、多路查找树

    1.二叉搜索树 1.1定义 是一棵二叉树,每个节点一定大于等于其左子树中每一个节点,小于等于其右子树每一个节点 1.2插入节点 从根节点开始向下找到合适的位置插入成为叶子结点即可:在向下遍历时,如果要 ...

  9. 二叉查找树(二叉排序树)的详细实现,以及随机平衡二叉查找树Treap的分析与应用

    这是一篇两年前写的东西,自我感觉还是相当不错的Treap教程.正好期末信息科学技术概论课要求交一个论文,就把这个东西修改了一下交了,顺便也发到这里吧. 随机平衡二叉查找树Treap的分析与应用 1.序 ...

随机推荐

  1. IDEA中的各种快捷键

    1.get.set快捷键: Alt+Insert 2.idea补全返回值快捷键 比如写了一个new User(),需要补全前面的User user ctrl+alt+V 3.idea全局搜索: Ctr ...

  2. Android-WebView支持input file启用相机/选取照片

    webview要调起input-file拍照或者选取文件功能,可以在webview.setWebChromeClient方法中重写指定的方法,来拦截webview的input事件,并做我们相应的操作. ...

  3. netcore mvc 的简单实现

    实现的功能 简单的路由系统 支持中间件 简单Filter支持 只支持HttpPost.HttpGet 使用Dotliquid做为视图渲染引擎 核心实现 HttpChannel 复制监听Tcp请求,并按 ...

  4. Qt 纯属娱乐-模拟一个导航定位系统

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://www.cnblogs.com/lihuidashen/p/115397 ...

  5. 【Rocketmq】通过 docker 快速搭建 rocketmq 环境

    1. 安装 Namesrv 拉取镜像 docker pull rocketmqinc/rocketmq:4.4.0` 启动容器 docker run -d -p 9876:9876 -v {RmHom ...

  6. python实现经典算法

    1,快速排序 题目形式:手写一下快速排序算法. 题目难度:中等. 出现概率:约50%.手写快排绝对是手撕代码面试题中的百兽之王,掌握了它就是送分题,没有掌握它就是送命题. 参考代码: def quic ...

  7. 控制执行流程之switch语句

    switch语句,同时也是一种选择性语句,其根据整数表达式的值(整数选择因子)选择一段代码去执行.整数选择因子为int类型或者char类型.

  8. 【PTA】浙江大学数据结构慕课 课后编程作业 03-树1 树的同构

    题目内容 给定两棵树T1和T2.如果T1可以通过若干次左右孩子互换就变成T2,则我们称两棵树是"同构"的.例如图1给出的两棵树就是同构的,因为我们把其中一棵树的结点A.B.G的左右 ...

  9. sql server 中常用修改列 ,创建主外键操作

    表结构 CREATE TABLE [staff] ( [id] [varchar](50) NOT NUL L, [name] [varchar](50) NOT NULL, [password] [ ...

  10. Unity项目 - DeathtrapDungeon死亡地牢

    目录 游戏原型 项目演示 绘图资源 代码实现 注意事项 技术探讨 参考来源 游戏原型 死亡地牢是一款 2D-Roguelike 的地牢冒险游戏.手握利刃,斩杀怪物,在凶险的地牢内生存下去.但注意,敌人 ...