前言

因为有些树是这样子的:

这样子的树有个坏处就是查询效率低,因为左边只有一层,而右边有3层,这就说明如果查找一个数字大于根元素的数字,那么查询判断就更多。

解决方法就是降低两边的层数差距:

变成这样。

那么如何这样做呢?

如果右边比左边层次大于1,进行左旋转规格:

如果左边比右边层次大于1,进行右旋转:

为什么是这样处理呢?

我拿左旋转来说:

左边是1,右边是3,那么需要右边的给一层给左边。

这时候就是6作为根节点,6作为根节点,本身呢,就是一颗二叉树。

这时候呢,6大于4,所以4在6的右边,而6的左节点肯定大于4(原理的根节点)小于6,所以作为4的左节点代替,4也就成了6的左节点。

右旋转同理。

旋转会出现的问题

就是这样通过右旋转之后,可能达不到目标。

看上面这张图,右旋转后还是不平衡,为什么会这样呢?原因是这样的,我们来看下本质问题,右旋转干了什么?右旋转其实是重新分配元素的过程。

看我这种图红色部分,其实是将红色部门分给了右边,这时候会存在元素个数差异过大的问题。

那么这时候该怎么办呢?

要让红框部分进行左旋转才行,那么看代码吧。

正文

节点模型:

public class Node
{
public Node left; public Node right; int value; public int Value { get => value; set => this.value = value; } public Node(int value)
{
this.Value = value;
}
//中序排序
public void infixOrder()
{
if (this.left != null)
{
this.left.infixOrder();
}
Console.WriteLine(this);
if (this.right != null)
{
this.right.infixOrder();
}
}
//左子树高度
public int leftHeight() {
if (left == null)
{
return 0;
}
return this.left.height();
}
//右子树高度
public int rightHeight() {
if (right == null)
{
return 0;
}
return this.right.height();
}
//计算高度
public int height()
{
return Math.Max(left==null?0:this.left.height(),right==null?0:this.right.height())+1;
}
//进行左旋转
private void leftRotate()
{
Node newNode = new Node(value);
newNode.left = left;
newNode.right = right.left;
//将右节点变成根节点
value =right.value;
//原先右节点被抛弃
right=right.right;
//左节点设置为
left = newNode;
}
//进行右旋转
private void rightRotate()
{
Node newNode = new Node(value);
newNode.left = left.right;
newNode.right = right;
value = this.left.value;
left = left.left;
right = newNode;
}
public override string ToString()
{
return Value.ToString();
}
//增加元素
public void addNode(Node node)
{
if (node.Value < this.Value)
{
if (this.left == null)
{
this.left = node;
}
else
{
this.left.addNode(node);
}
}
else {
if (this.right == null)
{
this.right = node;
}else {
this.right.addNode(node);
}
}
if (rightHeight() > leftHeight() + 1)
{
if (right.leftHeight() > right.rightHeight())
{
right.rightRotate(); }
this.leftRotate();
}
else if (leftHeight() > rightHeight() + 1)
{
if (left.rightHeight() > left.leftHeight())
{
left.leftRotate();
}
this.rightRotate();
}
}
public Node searchParentNode(int value)
{
if ((this.left != null && this.left.value == value || (this.right != null && this.right.value == value)))
{
return this;
}
else
{
if (this.value < value && this.left != null)
{
return this.left.searchParentNode(value);
}
else if (this.value >= value && this.right != null)
{
return this.right.searchParentNode(value);
}
else
{
return null;
}
}
}
//查找元素
public Node searchNode(int value)
{
if (this.Value == value)
{
return this;
}
if (this.Value > value)
{
if (this.left != null)
{
return this.right.searchNode(value);
}
else
{
return null;
}
}
else
{
if (this.left != null)
{
return this.left.searchNode(value);
}
else
{
return null;
}
}
}
}

树模型:

public class BinarySortTree
{
//根节点
Node root; internal Node Root { get => root; set => root = value; } public BinarySortTree(Node root)
{
this.Root = root;
} public BinarySortTree() : this(null)
{ } public void add(Node node)
{
if (Root == null)
{
Root = node;
}
else
{
this.Root.addNode(node);
}
} public void infixOrder()
{
if (Root == null)
{
Console.WriteLine("root 为空");
}
else
{
Root.infixOrder();
}
} public Node searchNode(int value)
{
if (Root==null)
{
Console.WriteLine("root 为空");
}
return Root.searchNode(value);
} public int delRightTreeMin(Node node)
{
Node tagert = node;
while (tagert.left!=null)
{
tagert = tagert.left;
}
delNode(tagert.Value);
return tagert.Value;
} public Node searchParentNode(int value)
{
if (Root != null)
{
return Root.searchParentNode(value);
}
return null;
} public void delNode(int value)
{
if (Root == null)
{
return;
}
Node node=searchNode(value);
if (node == null)
{
return;
}
if (node.Value == Root.Value)
{
Root = null;
return;
}
Node parent = searchParentNode(value);
if (node.left == null && node.right == null)
{
if (parent.left.Value == value)
{
parent.left = null;
}
else
{
parent.right = null;
}
}
else if (node.left != null && node.right != null)
{
//删除左边最大值或者右边最小值,然后修改值为删除的值
parent.right.Value=delRightTreeMin(node.right);
}
else
{
if (node.left != null)
{
if (parent.left.Value == value)
{
parent.left = node.left;
}
else
{
parent.right = node.left;
}
}
else {
if (parent.left.Value == value)
{
parent.left = node.right;
}
else
{
parent.right = node.right;
}
}
}
}
}

测试代码:

static void Main(string[] args)
{
int[] arr = { 10, 11, 7, 6, 8, 9 };
BinarySortTree binarySortTree = new BinarySortTree();
//循环的添加结点到二叉排序树
for (int i = 0; i < arr.Length; i++)
{
binarySortTree.add(new Node(arr[i]));
}
//中序遍历后的数据
Console.WriteLine("树的高度=" + binarySortTree.Root.height()); //3
Console.WriteLine("树的左子树高度=" + binarySortTree.Root.leftHeight()); // 2
Console.WriteLine("树的右子树高度=" + binarySortTree.Root.rightHeight()); // 2
Console.WriteLine("当前的根结点=" + binarySortTree.Root);//8
Console.Read();
}

结果:

重新整理数据结构与算法(c#)—— 平衡二叉树[二十三]的更多相关文章

  1. C#数据结构与算法系列(二十三):归并排序算法(MergeSort)

    1.介绍 归并排序(MergeSort)是利用归并的思想实现的排序方法,该算法采用经典的分治策略(分治法将问题分(divide)成一些小的问题然后递归求解, 而治(conquer)的阶段则将分的阶段得 ...

  2. 数据结构与算法--从平衡二叉树(AVL)到红黑树

    数据结构与算法--从平衡二叉树(AVL)到红黑树 上节学习了二叉查找树.算法的性能取决于树的形状,而树的形状取决于插入键的顺序.在最好的情况下,n个结点的树是完全平衡的,如下图"最好情况&q ...

  3. Java数据结构与算法解析(十二)——散列表

    散列表概述 散列表就是一种以 键-值(key-indexed) 存储数据的结构,我们只要输入待查找的值即key,即可查找到其对应的值. 散列表的思路很简单,如果所有的键都是整数,那么就可以使用一个简单 ...

  4. 数据结构与算法16—平衡二叉(AVL)树

    我们知道,对于一般的二叉搜索树(Binary Search Tree),其期望高度(即为一棵平衡树时)为log2n,其各操作的时间复杂度O(log2n)同时也由此而决定.但是,在某些极端的情况下(如在 ...

  5. Java数据结构和算法(十二)——2-3-4树

    通过前面的介绍,我们知道在二叉树中,每个节点只有一个数据项,最多有两个子节点.如果允许每个节点可以有更多的数据项和更多的子节点,就是多叉树.本篇博客我们将介绍的——2-3-4树,它是一种多叉树,它的每 ...

  6. 数据结构与算法问题 AVL二叉平衡树

    AVL树是带有平衡条件的二叉查找树. 这个平衡条件必须保持,并且它必须保证树的深度是O(logN). 一棵AVL树是其每一个节点的左子树和右子树的高度最多差1的二叉查找树. (空树的高度定义为-1). ...

  7. C#数据结构与算法系列(二十):插入排序算法(InsertSort)

    1.介绍 插入排序算法属于内部排序算法,是对于欲排序的元素以插入的方式找寻该元素的适当位置,以达到排序的目的 2.思想 插入排序(Insertion Sorting)的基本思想是:把n个待排序的元素看 ...

  8. C#数据结构与算法系列(二十一):希尔排序算法(ShellSort)

    1.介绍 希尔排序是希尔(Donald Shell)于1959年提出的一种排序算法.希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序. 2.基本思想 希尔排 ...

  9. 重新整理数据结构与算法(c#)—— 图的深度遍历和广度遍历[十一]

    参考网址:https://www.cnblogs.com/aoximin/p/13162635.html 前言 简介图: 在数据的逻辑结构D=(KR)中,如果K中结点对于关系R的前趋和后继的个数不加限 ...

  10. C#数据结构与算法系列(二):稀疏数组(SparseArray)

    1.介绍 当一个数组中大部分元素为0,或者为同一个值的数组时,可以使用稀疏数组来保存该数组.    稀疏数组的处理方法是: 1.记录数组一共有几行几列,有多少个不同的值    2.把具有不同值的元素的 ...

随机推荐

  1. python用matplotlib或boxplot作图的时候,中文标注无法正常显示,乱码为小方框的解决办法

    第一种 import matplotlib.pyplot as plt plt.rc("font",family="SimHei",size="22& ...

  2. 独家消息:阿里云悄然推出RPA云电脑,已与多家RPA厂商开放合作

    独家消息:阿里云悄然推出RPA云电脑,已与多家RPA厂商开放合作 RPA云电脑,让RPA开箱即用算力无限? 文/王吉伟 这几天,王吉伟频道通过业内人士获得独家消息,阿里云近期推出了一个名为「RPA云电 ...

  3. 1、eureka的注册流程

    客户端注册到服务端是通过http请求的 涉及到多级缓存 register注册表 源码精髓:多级缓存设计思想 在拉取注册表的时候: 首先从ReadOnlyCacheMap里查缓存的注册表. 若没有,就找 ...

  4. slice 切片数组测试记录【GO 基础】

    〇.测试前准备 本文是在 GO 环境下测试记录系列之一,GO 基本环境部署步骤将略过,直接上代码. 下面是常用命令:[初始化 + 运行 + 编译] // {GOPATH} 环境变量值, example ...

  5. 使用Zabbix监控openstack的系统资源

    1 概述 OpenStack是一个开源的云计算管理平台项目,是一系列软件开源项目的组合.由NASA(美国国家航空航天局)和Rackspace合作研发并发起,以Apache许可证(Apache软件基金会 ...

  6. flomo 窗口置顶 - 通用方法 autohotkey

    需求 开网页的时候需要记录一些东西 想一直显示 操作 要安装 https://www.autohotkey.com/ 创建个 .ahk 文件 运行下 快捷键是 alt+小键盘8 ;置顶当前窗口 !Nu ...

  7. vxe table columns 要用data里的值,用computed的值会导致排序部分不好用。

    vxe table columns 要用data里的值,用computed的值会导致排序部分不好用.

  8. 13_AAC编码介绍

    AAC(Advanced Audio Coding,译为:高级音频编码),是由Fraunhofer IIS.杜比实验室.AT&T.Sony.Nokia等公司共同开发的有损音频编码和文件格式. ...

  9. 实时云渲染:流式传输 VR 和 AR 内容

    想象一下无需专用的物理计算机,甚至无需实物连接,就能获得高质量的 AR/VR 体验是种什么样的体验? 过去,与 VR 交互需要专用的高端工作站,并且根据头显.壁挂式传感器和专用的物理空间.VR 中的复 ...

  10. C# 12 拦截器 Interceptors

    拦截器Interceptors是一种可以在编译时以声明方式替换原有应用的方法. 这种替换是通过让Interceptors声明它拦截的调用的源位置来实现的. 您可以使用拦截器作为源生成器的一部分进行修改 ...