【数据结构与算法】二叉搜索树和平衡二叉树

news2024/10/12 8:26:35

二叉搜索树

左子树的结点都比当前结点小,右子树的结点都比当前结点大。

构造二叉搜索树:

let arr = [3, 4, 7, 5, 2]

function Node(value) {
    this.value = value
    this.left = null
    this.right = null
}

/**
 * 添加结点
 * @param root 当前结点
 * @param num 新的结点的值
 */
function addNode(root, num) {
    if (root == null) return
    if (root.value == num) return
    if (root.value < num) {
        if (root.right == null) root.right = new Node(num)
        else addNode(root.right, num)
    } else {
        if (root.left == null) root.left = new Node(num)
        else addNode(root.left, num)
    }
}

function binarySearchTree(arr) {
    if (arr == null || arr.length == 0) return null
    let root = new Node(arr[0])
    for (let i = 1; i < arr.length; i++) {
        addNode(root, arr[i])
    }
    return root
}
let root = binarySearchTree(arr)
console.dir(root)

image.png

二叉搜索树的应用:

let arr = [3, 4, 7, 5, 2]

function Node(value) {
    this.value = value
    this.left = null
    this.right = null
}

/**
 * 添加结点
 * @param root 当前结点
 * @param num 新的结点的值
 */
function addNode(root, num) {
    if (root == null) return
    if (root.value == num) return
    if (root.value < num) {
        if (root.right == null) root.right = new Node(num)
        else addNode(root.right, num)
    } else {
        if (root.left == null) root.left = new Node(num)
        else addNode(root.left, num)
    }
}

function binarySearchTree(arr) {
    if (arr == null || arr.length == 0) return null
    let root = new Node(arr[0])
    for (let i = 1; i < arr.length; i++) {
        addNode(root, arr[i])
    }
    return root
}

function searchByTree (root,target) {
    if (root == null) return false
    if (root.value == target) return true
    if (root.value > target) return searchByTree(root.left, target)
    else return searchByTree(root.right, target)
}

let root = binarySearchTree(arr)
console.log(searchByTree(root, 4))

平衡二叉树

  • 根节点的左子树和右子树的高度处不能超过1
  • 每棵子树都符合上述规则

image.png

function Node(value) {
    this.value = value
    this.left = null
    this.right = null
}

let a = new Node('a')
let b = new Node('b')
let c = new Node('c')
let d = new Node('d')
let e = new Node('e')
let f = new Node('f')
let g = new Node('g')
let h = new Node('h')
let j = new Node('j')
a.left = b
a.right = c
b.left = d
b.right = e
c.left = f
c.right = g
d.right = h
e.right = j

function getDeep(root) {
    if (root == null) return 0
    let leftDeep = getDeep(root.left)
    let rightDeep = getDeep(root.right)
    return Math.max(leftDeep, rightDeep) + 1
}

function isBalance(root) {
    if (root == null) return true
    let leftDeep = getDeep(root.left)
    let rightDeep = getDeep(root.right)
    if (Math.abs(leftDeep - rightDeep) > 1) {
        return false
    } else {
        return isBalance(root.left) && isBalance(root.right)
    }
}

console.log(getDeep(a))
console.log(isBalance(a))

单旋

某一节点不平衡,如果左边浅,右边深,进行左单旋。

image.png

  • 旋转节点:不平衡的节点为旋转节点(2)
  • 新根:旋转之后称为根节点的节点(5)
  • 变化分支:父级节点发生变化的那个分支
  • 不变分支:父级节点不变的那个分支

左单旋时:

  • 旋转节点:当前不平衡的节点 2
  • 新根:右子树的根节点 5
  • 变化分支:旋转节点的右子树的左子树 3
  • 不变分支:旋转节点的右子树的右子树 6

image.png

右单旋时:

  • 旋转节点:当前不平衡的节点 6
  • 新根:左子树的根节点 3
  • 变化分支:旋转节点的左子树的右子树 5
  • 不变分支:旋转节点的左子树的左子树 2

步骤:

  • 进行左单旋
    1. 找到新根
    1. 找到变化分支
    1. 当前旋转节点的右孩子为变化分支
    1. 新根的左孩子为旋转节点
    1. 返回新的根节点
  • 进行右单旋
    1. 找到新根
    1. 找到变化分支
    1. 当前旋转节点的左孩子为变化分支
    1. 新根的右孩子为旋转节点
    1. 返回新的根节点
function Node(value) {
    this.value = value
    this.left = null
    this.right = null
}

let node2 = new Node('2')
let node5 = new Node('5')
let node3 = new Node('3')
let node6 = new Node('6')
node2.right= node5
node5.left = node3
node5.right = node6

function getDeep(root) {
    if (root == null) return 0
    let leftDeep = getDeep(root.left)
    let rightDeep = getDeep(root.right)
    return Math.max(leftDeep, rightDeep) + 1
}

function isBalance(root) {
    if (root == null) return true
    let leftDeep = getDeep(root.left)
    let rightDeep = getDeep(root.right)
    if (Math.abs(leftDeep - rightDeep) > 1) {
        return false
    } else {
        return isBalance(root.left) && isBalance(root.right)
    }
}

function leftRotate (root){
    // 找到新根
    let newRoot= root.right
    // 找到变化分支
    let changeBranch  = root.right.left
    // 当前旋转接点的右节点变为变化分支
    root.right = changeBranch
    // 新根的左节点变为旋转分支
    newRoot.left = root
    // 返回新的根节点
    return newRoot
}
function rightRotate (root){
    let newRoot = root.left
    let changeBranch = root.left.right
    root.left = changeBranch
    newRoot.right = root
    return newRoot
}

/**
 * 平衡二叉树
 * @param root 根节点
 * @returns {*|boolean} 平衡后的根节点
 */
function change(root) {
    if (isBalance(root)) return root
    if (root.left != null) root.left = change(root.left)
    if (root.right != null) root.right = change(root.right)
    let leftDeep = getDeep(root.left)
    let rightDeep = getDeep(root.right)
    if (Math.abs(leftDeep - rightDeep < 2)) {
        return true
    } else if (leftDeep > rightDeep) {
        // 左边深 右旋
        return rightRotate(root)
    }  else {
        // 右边深 左旋
       return  leftRotate(root)
    }
}

console.log(isBalance(node2))
let newRoot = change(node2)
console.log(isBalance(newRoot))

双旋

有些情况只有一次单选是实现不了的,比如下面的情况:

变化分支(6,7)不可以是唯一的最深分支。

image.png

如果变化分支(6,7)是唯一的最深分支,那么需要先进行依次左旋,再进行右旋。也就是需要双旋。

image.png

二叉树的双旋(左右双旋,右左双旋)

当要对某个节点进行左单旋时,如果变化分支是唯一的最深分支,那么我们要对新根进行右单旋,然后再进行左单旋,这样的旋转叫做右左双旋

当要对某个节点进行右单旋时,如果变化分支是惟一的最深分支,那么我们要对新根进行左单旋,然后再进行右单旋,这样的旋转叫做左右双旋

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1567416.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

50道Java经典面试题总结

1、那么请谈谈 AQS 框架是怎么回事儿&#xff1f; &#xff08;1&#xff09;AQS 是 AbstractQueuedSynchronizer 的缩写&#xff0c;它提供了一个 FIFO 队列&#xff0c;可以看成是一个实现同步锁的核心组件。 AQS 是一个抽象类&#xff0c;主要通过继承的方式来使用&#x…

AI绘图:Stable Diffusion WEB UI 详细操作介绍:基础篇

接上一篇《AI绘图体验&#xff1a;Stable Diffusion本地化部署详细步骤》本地部署完了SD后&#xff0c;大家肯定想知道怎么用&#xff0c;接下来补一篇Stable Diffusion WEB UI 详细操作&#xff0c;如果大家还没有完成SD的部署&#xff0c;请参考上一篇文章进行本地化的部署。…

抽象类与接口(3)(接口部分)

❤️❤️前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; hellohello~&#xff0c;大家好&#x1f495;&#x1f495;&#xff0c;这里是E绵绵呀✋✋ &#xff0c;如果觉得这篇文章还不错的话还请点赞❤️❤️收藏&#x1f49e; &#x1f49e; 关注&#x1f4a5;&…

Spring Boot:Web开发之视图模板技术的整合

Spring Boot 前言Spring Boot 整合 JSPSpring Boot 整合 FreeMarkerSpring Boot 整合 ThymeleafThymeleaf 常用语法 前言 在 Web 开发中&#xff0c;视图模板技术&#xff08;如 JSP 、FreeMarker 、Thymeleaf 等&#xff09;用于呈现动态内容到用户界面的工具。这些技术允许开…

【css】使用display:inline-block后,元素间存在4px的间隔

问题&#xff1a;在本地项目中使用【display: inline-block】&#xff0c;元素间存在4px间隔。打包后发布到外网又不存在这个问题了。 归根结底这是一个西文排版的问题&#xff0c;英文有空格作为词分界&#xff0c;而中文则没有。 此时的元素具有文本属性&#xff0c;只要标签…

RUST语言函数的定义与调用

1.定义函数 定义一个RUST函数使用fn关键字 函数定义语法: fn 函数名(参数名:参数类型,参数名:参数类型) -> 返回类型 { //函数体 } 定义一个没有参数,没有返回类型的参数 fn add() {println!("调用了add函数!"); } 定义有一个参数的函数 fn add(a:u32)…

android framework 学习笔记(1)

学习资料&#xff1a;《Android Framework 开发揭秘》_哔哩哔哩_bilibili 什么是android framework 看图说话&#xff0c;android框架从上至下分为&#xff1a; 应用层(Application)&#xff0c;Java framework(Application Framework),Native framework. 包括Libraries 和 A…

vue弹出的添加信息组件中 el-radio 单选框无法点击问题

情景描述:在弹出的添加信息的组件中的form中有一个单选框,单选框无法进行点击切换 原因如下: 单选框要求有个默认值,因为添加和更新操作复用同一个组件,所以我在初始化时对相关进行了判定,如果为空则赋初始值 结果这样虽然实现了初始值的展示,但是就是如此造成了单选框的无法切…

电商技术揭秘一:电商架构设计与核心技术

文章目录 引言一、电商平台架构概述1.1 架构设计原则与架构类型选择1.2 传统电商平台架构与现代化架构趋势分析 二、高并发处理与负载均衡2.1 高并发访问特点分析与挑战2.2 负载均衡原理与算法选择 三、分布式数据库与缓存技术3.1 分布式数据库设计与一致性考量3.2 缓存策略与缓…

(4)(4.6) Triducer

文章目录 前言 1 安装triducer 2 故障排除 3 参数说明 前言 Triducer 集速度、温度和深度传感器于一体。埃文在这篇 ardupilot.org 博文底部提供了这些说明(Evan at the bottom of this ardupilot.org blog post)。 1 安装triducer 下面的示例提供了在 Pixhawk 上安装 tri…

postgis 建立路径分析,使用arcmap处理路网数据,进行拓扑检查

在postgresql+postgis上面,对路网进行打断化简,提高路径规划成功率。 一、创建空间库以及空间索引 CREATE EXTENSION postgis; CREATE EXTENSION pgrouting; CREATE EXTENSION postgis_topology; CREATE EXTENSION fuzzystrmatch; CREATE EXTENSION postgis_tiger_geocoder;…

58商铺全新UI试客试用平台网站php源码

探索未来商铺新纪元&#xff0c;58商铺全新UI试客试用平台网站PHP源码完整版震撼来袭&#xff01; 在这个数字化飞速发展的时代&#xff0c;58商铺一直致力于为商家和消费者打造更加便捷、高效的交易平台。今天&#xff0c;我们荣幸地推出全新UI试客试用平台网站PHP源码完整版…

springboot 项目整合easy-captcha验证码功能

效果 1、验证码使用easy-captcha,在pom文件增加依赖 <!-- google 验证码 --><dependency><groupId>com.github.whvcse</groupId><artifactId>easy-captcha</artifactId></dependency> 2、增加获取kaptcha的ctrl package com.*.*.s…

pygame三角形重心坐标填充 沿x轴旋转

import pygame from pygame.locals import * import sys import math# 初始化Pygame pygame.init()# 设置窗口大小 width, height 800, 600 screen pygame.display.set_mode((width, height)) pygame.display.set_caption(3D Triangle Fill with Barycentric Coordinates)# 定…

LVS、HAProxy

集群&#xff1a;将很多个机器组织到一起&#xff0c;作为一个整体对外提供服务。集群在扩展性、性能方面都可以做到很灵活。集群的分类&#xff1a;负载均衡集群&#xff1a;Load Balance。高可用集群&#xff1a;High Available。高性能集群&#xff1a;High Performance Com…

MyBatis的基本应用

源码地址 01.MyBatis环境搭建 添加MyBatis的坐标 <!--mybatis坐标--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.9</version></dependency><!--mysql驱动坐…

【大模型】大模型 CPU 推理之 llama.cpp

【大模型】大模型 CPU 推理之 llama.cpp llama.cpp安装llama.cppMemory/Disk RequirementsQuantization测试推理下载模型测试 参考 llama.cpp 描述 The main goal of llama.cpp is to enable LLM inference with minimal setup and state-of-the-art performance on a wide var…

开源模型应用落地-chatglm3-6b模型小试-入门篇(二)

一、前言 刚开始接触AI时&#xff0c;您可能会感到困惑&#xff0c;因为面对众多开源模型的选择&#xff0c;不知道应该选择哪个模型&#xff0c;也不知道如何调用最基本的模型。但是不用担心&#xff0c;我将陪伴您一起逐步入门&#xff0c;解决这些问题。 在信息时代&#xf…

量化交易入门(四十一)ASI指标Python实现和回测

老规矩先上图&#xff0c;看看ASI指标使用苹果数据回测后的结果如何。 一、策略运行结果 执行的结果&#xff1a; Starting Portfolio Value: 100000.00 Final Portfolio Value: 92514.82 Annualized Return: -1.93% Sharpe Ratio: -0.27 Max Drawdown: 25.34% Max Drawdown …

EasyBoss ERP支持TikTok Shop拆单发货功能,多店铺订单包裹拆分一个系统搞定

一些TikTok Shop本土卖家在运营过程中面临这样的困境&#xff1a;当顾客一次性订购多个商品&#xff0c;由于部分商品缺货或包裹超重&#xff0c;订单延迟发货或被物流限制发不出货&#xff0c;导致店铺被投诉。为了解决这一问题&#xff0c;卖家可以采取拆单发货的策略&#x…