几何数据结构之四叉树与八叉树

几何数据结构之四叉树与八叉树

    • 四叉树的定义
    • 四叉树深度的计算公式推导
      • 假设:
      • 计算过程:
        • 1. 划分空间:
        • 2. 节点容纳的最小距离:
        • 3. 解出深度:
        • 4. 考虑常数项:
      • 总结:
    • 八叉树

四叉树的定义

四叉树(Quadtree):是一颗包含树根(Root)的树,每个节点包含四个子节点。我们常见用于描述四叉树的形式如下图:

在这里插入图片描述
它一个节点可以存储四个子节点的数据。当然,我们还可以采用其他形式来描述四叉树。

如下图所示:

在这里插入图片描述
树中每个节点有四个子节点,我们可以把每个节点看作一个正方形,最外围的正方形,包含四个第二大的正方形,同样的,第二大的也包含四个第三大正方形。
当然,我们可以以象限来描述它,一个正方形,被十字叉分为了四个象限,一个象限就代表了一个节点,我们可以不断的递归的去取正方形的边长中点,直到正方形中只存在一个点的。

四叉树是一种空间划分数据结构,通常用于存储二维平面中的点。四叉树将空间递归地划分为四个象限(子区域),并将点插入到对应的子区域中。

插入过程:
每个节点表示一个矩形区域,如果该区域可以容纳更多的点,它就会插入点;如果已经达到阈值(例如最多只能容纳 4 个点),则会进行分割,将区域划分为 4 个子区域。
子区域的插入:每当点超出当前节点的边界时,应该递归到下一个合适的子区域中。如果某个点仍然位于当前节点的范围内,则将其插入到当前节点。
子节点的划分:当节点满了(即点数量超过设定阈值),它将自己分割成 4 个子节点,每个子节点管理一个象限的区域。

四叉树深度的计算公式推导

假设:

  1. 初始区域的边长是 ( S )。
  2. 每个节点容纳的最小距离是 ( C ),即空间中任意两个点之间的最小距离为 ( C ),所以每个子区域将尽可能容纳这种最小距离的点。

计算过程:

1. 划分空间:

四叉树的每一层将空间分为四个象限(子区域)。如果在深度为 ( d ) 的层次,空间被划分为 ( 4^d ) 个子区域,那么每个子区域的大小将是:

子区域边长 = S 2 d \text{子区域边长} = \frac{S}{2^d} 子区域边长=2dS

2. 节点容纳的最小距离:

每个子区域容纳的最小点间距是 ( C ),所以在每个子区域中,至少应该有足够的空间容纳这种最小距离的点。为了确保每个子区域中有两个点之间的最小距离不小于 ( C ),我们有以下条件:

S 2 d ≥ C \frac{S}{2^d} \geq C 2dSC

这意味着每个子区域的边长必须大于等于最小点距离 ( C ),否则就需要进一步划分。

3. 解出深度:

根据上面的不等式,求得最小深度 ( d ):

S 2 d ≥ C ⇒ 2 d ≤ S C ⇒ d ≤ log ⁡ 2 ( S C ) \frac{S}{2^d} \geq C \quad \Rightarrow \quad 2^d \leq \frac{S}{C} \quad \Rightarrow \quad d \leq \log_2 \left( \frac{S}{C} \right) 2dSC2dCSdlog2(CS)

4. 考虑常数项:

实际应用中,可能还需要考虑额外的调整常数项,通常为 3 2 \frac{3}{2} 23,用于补偿一些实现中的空间管理开销或其他实际细节。因此,四叉树的深度可以表示为:

深度 = log ⁡ 2 ( S C ) + 3 2 \text{深度} = \log_2 \left( \frac{S}{C} \right) + \frac{3}{2} 深度=log2(CS)+23

总结:

  • ( S ) 是初始区域的边长。
  • ( C ) 是任意两个点之间的最小距离,即每个子区域内元素间的最小间距。
  • 最终的四叉树深度大致为:

深度 = log ⁡ 2 ( S C ) + 3 2 \text{深度} = \log_2 \left( \frac{S}{C} \right) + \frac{3}{2} 深度=log2(CS)+23

四叉树大量使用与碰撞检测上面。

下面是简单实现四叉树的代码:

#include <iostream>
#include <vector>
#include <memory>

using namespace std;

// 定义一个点(Point)类,表示一个二维空间中的点
class Point
{
public:
    int x, y;  // 点的 x 和 y 坐标

    // 构造函数,初始化 x 和 y 坐标
    Point(int x, int y) : x(x), y(y) {}
};

// 定义一个边界(Boundary)类,表示一个矩形区域
class Boundary
{
public:
    int x, y, width, height;  // 矩形的中心坐标 (x, y),宽度和高度

    // 构造函数,初始化矩形的中心坐标、宽度和高度
    Boundary(int x, int y, int width, int height)
        : x(x), y(y), width(width), height(height) {}

    // 判断一个点是否在矩形区域内
    bool contains(const Point& point) const
    {
        return point.x >= x - width / 2 && point.x <= x + width / 2 &&
            point.y >= y - height / 2 && point.y <= y + height / 2;
    }

    // 判断一个矩形区域是否与当前矩形区域相交
    bool intersects(const Boundary& range) const
    {
        return !(range.x - range.width / 2 > x + width / 2 ||
            range.x + range.width / 2 < x - width / 2 ||
            range.y - range.height / 2 > y + height / 2 ||
            range.y + range.height / 2 < y - height / 2);
    }
};

// 定义四叉树(QuadTree)类,用于存储二维空间中的点
class QuadTree
{
private:
    Boundary boundary;  // 当前节点的矩形区域
    int capacity;  // 当前节点的最大容量(每个节点最多存储多少个点)
    vector<Point> points;  // 当前节点存储的点
    unique_ptr<QuadTree> northeast;  // 东北子节点
    unique_ptr<QuadTree> northwest;  // 西北子节点
    unique_ptr<QuadTree> southeast;  // 东南子节点
    unique_ptr<QuadTree> southwest;  // 西南子节点

public:
    // 构造函数,初始化边界和容量
    QuadTree(const Boundary& boundary, int capacity)
        : boundary(boundary), capacity(capacity) {}

    // 将当前节点划分为四个子节点
    void subdivide()
    {
        int subWidth = boundary.width / 2;  // 子节点的宽度
        int subHeight = boundary.height / 2;  // 子节点的高度
        int x = boundary.x;  // 父节点的 x 坐标
        int y = boundary.y;  // 父节点的 y 坐标

        // 创建四个子节点,分别对应东北、西北、东南、西南
        northeast = make_unique<QuadTree>(Boundary(x + subWidth / 2, y - subHeight / 2, subWidth, subHeight), capacity);
        northwest = make_unique<QuadTree>(Boundary(x - subWidth / 2, y - subHeight / 2, subWidth, subHeight), capacity);
        southeast = make_unique<QuadTree>(Boundary(x + subWidth / 2, y + subHeight / 2, subWidth, subHeight), capacity);
        southwest = make_unique<QuadTree>(Boundary(x - subWidth / 2, y + subHeight / 2, subWidth, subHeight), capacity);
    }

    // 插入一个点到四叉树中
    bool insert(const Point& point)
    {
        // 如果点超出了当前节点的边界,返回 false
        if (!boundary.contains(point))
        {
            cout << "Point (" << point.x << ", " << point.y << ") is out of bounds." << endl;
            return false;
        }

        // 如果当前节点的点数未超出容量,则直接插入点
        if (points.size() < capacity)
        {
            points.push_back(point);  // 将点加入当前节点的点集合中
            cout << "Point (" << point.x << ", " << point.y << ") inserted in current node." << endl;
            return true;
        }

        // 如果当前节点已超出容量,进行分割
        if (northeast == nullptr)
        {
            cout << "Subdividing node at (" << boundary.x << ", " << boundary.y << ")" << endl;
            subdivide();  // 划分子节点
        }

        // 递归地将点插入到四个子节点中
        if (northeast->insert(point)) return true;
        if (northwest->insert(point)) return true;
        if (southeast->insert(point)) return true;
        if (southwest->insert(point)) return true;

        return false;
    }

    // 打印当前节点及其所有子节点中的点
    void printPoints() const
    {
        // 打印当前节点存储的点
        for (const auto& point : points)
        {
            cout << "(" << point.x << ", " << point.y << ")" << endl;
        }

        // 递归地打印四个子节点中的点
        if (northeast != nullptr) northeast->printPoints();
        if (northwest != nullptr) northwest->printPoints();
        if (southeast != nullptr) southeast->printPoints();
        if (southwest != nullptr) southwest->printPoints();
    }
};

int main()
{
    // 扩大根节点的边界为 20x20
    Boundary boundary(0, 0, 20, 20); 

    // 创建一个四叉树,根节点的容量为 4
    QuadTree qt(boundary, 4);

    // 插入一些点到四叉树
    qt.insert(Point(2, 3));
    qt.insert(Point(3, 5));
    qt.insert(Point(6, 7));
    qt.insert(Point(8, 1));
    qt.insert(Point(1, 1));
    qt.insert(Point(4, 4));

    // 打印树中的所有点
    cout << "Points in the tree:" << endl;
    qt.printPoints();

    return 0;
}

八叉树

八叉树原理与四叉树类似,它可以理解为一个下图:
在这里插入图片描述
一个立方体被三个平面分成了八块,它的实现与原理与四叉树类似,在此不过多赘述。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

机器学习中的方差与偏差

文章目录 方差与偏差1.1 数据1.1.1 数据的分布1.1.2 拟合 1.2 方差与偏差1.2.1 泛化误差的拆分1.2.2 理解方差偏差 1.3 方差-偏差trade-off1.3.1 方差-偏差trade-off1.3.2 方差与偏差诊断 1.4 降低策略1.4.1 噪声1.4.2 高偏差1.4.3 高方差 方差与偏差 1.1 数据 1.1.1 数据的分…

Weblogic - General - 弱口令 任意文件读取漏洞

0x01&#xff1a;漏洞简介 首先需要说明&#xff0c;本文并不是介绍了 Weblogic 某一 CVE 漏洞&#xff0c;而是提供了一种通用的测试思路。 0x0101&#xff1a;弱口令漏洞 弱口令漏洞主要是由于用户安全意识淡薄&#xff0c;为了便于记忆&#xff0c;设置了强度过低的密码&…

C#中的语句

C#提供了各式各样的语句&#xff0c;大多数是由C和C发展而来&#xff0c;当然&#xff0c;在C#中做了相应修改。语句和表达式一样&#xff0c;都是C#程序的基本组成部分&#xff0c;在本文我们来一起学习C#语句。 1.语句 语句是构造所有C#程序的过程构造块。在语句中可以声明…

VLAN基础理论

VLAN V&#xff1a;Virtual(虚拟) LAN ——局域网 VLAN ——虚拟局域网(虚拟广播域&#xff1a;交换机和路由器协同工作后&#xff0c;将原来的一个广播域&#xff0c;逻辑上切分为多个。) VLAN的配置我们基于以下拓扑进行&#xff1a; PC1-4的IP地址依次为192.168.1.1-192.168…

基于SpringBoot的健身房管理系统【源码+文档+部署讲解】

系统介绍 基于SpringBootVue实现的健身房管理系统采用前后端分离架构方式&#xff0c;系统设计了管理员、会员、员工三种角色&#xff0c;系统实现了用户登录与注册、个人中心、会员管理、员工管理、会员卡管理、会员卡类型管理、教练信息管理、解聘管理、健身项目管理、指导项…

C++ 模拟真人鼠标轨迹算法 - 防止游戏检测

一.简介 鼠标轨迹算法是一种模拟人类鼠标操作的程序&#xff0c;它能够模拟出自然而真实的鼠标移动路径。 鼠标轨迹算法的底层实现采用C/C语言&#xff0c;原因在于C/C提供了高性能的执行能力和直接访问操作系统底层资源的能力。 鼠标轨迹算法具有以下优势&#xff1a; 模拟…

搭建一个基于Spring Boot的校园台球厅人员与设备管理系统

搭建一个基于Spring Boot的校园台球厅人员与设备管理系统可以涵盖多个功能模块&#xff0c;例如用户管理、设备管理、预约管理、计费管理等。以下是一个简化的步骤指南&#xff0c;帮助你快速搭建一个基础的系统。 — 1. 项目初始化 使用 Spring Initializr 生成一个Spring …

Langchain+FastApi+Vue前后端Ai对话(超详细)

一、引入 首先可以先看下作者的文章 FastApi相关文章&#xff1a;创建最简单FastApi的项目Vue相关文章&#xff1a;最简单的aixos二次封装Langchain相关文章&#xff1a;如何使用LangSmith跟踪deepseek模型 二、后端搭建 1 项目文件结构 routers&#xff1a;存放api接口se…

图像去雾数据集的下载和预处理操作

前言 目前&#xff0c;因为要做对比实验&#xff0c;收集了一下去雾数据集&#xff0c;并且建立了一个数据集的预处理工程。 这是以前我写的一个小仓库&#xff0c;我决定还是把它用起来&#xff0c;下面将展示下载的路径和数据处理的方法。 下面的代码均可以在此找到。Auo…

Java中json的一点理解

一、Java中json字符串与json对象 1、json本质 json是一种数据交换格式。 常说的json格式的字符串 > 发送和接收时都只是一个字符串&#xff0c;它遵循json这种格式。 2、前后端交互传输的json是什么&#xff1f; 前后端交互传输的json都是json字符串 比如&#xff1a;…

React实现拖拽特效

前言 最近&#xff0c;我看到一个工程师的个人网站上&#xff0c;采用了拖拽作品集的互动特效&#xff0c;既有趣又吸引眼球。经过一些研究&#xff0c;我发现其实借助一些现成的套件&#xff0c;就能轻松实现这样的效果。今天就带大家一起看看&#xff0c;如何通过 Framer Mo…

leetcode904-水果成篮

leetcode 904 时间复杂度&#xff1a;O(n) 空间复杂度&#xff1a;O(1) 之前发布了一个滑动窗口的题目解答思路&#xff0c;参考博文&#xff1a;长度最小的子数组 本题也是基于滑动窗口的一个扩展题&#xff0c;主要解决方法是利用滑动窗口哈希表 var totalFruit function…

线性代数概述

矩阵与线性代数的关系 矩阵是线性代数的研究对象之一&#xff1a; 矩阵&#xff08;Matrix&#xff09;是一个按照长方阵列排列的复数或实数集合&#xff0c;是线性代数中的核心概念之一。矩阵的定义和性质构成了线性代数中矩阵理论的基础&#xff0c;而矩阵运算则简洁地表示和…

李宏毅机器学习HW1: COVID-19 Cases Prediction

Kaggle数据集和提交链接 特征选择&#xff08;主要修改地方&#xff09; 在sample code的基础上主要修改了Select_feat选择特征函数。 首先&#xff0c;因为数据集中的第一列是id&#xff0c;先在raw_x_train&#xff0c;raw_x_valid&#xff0c;raw_x_test中都去掉这一列。其…

owasp SQL 注入-03 (原理)

1: 先看一下注入界面: 点submit 后&#xff0c;可以看到有语法报错&#xff0c;说明已经起作用了: 报如下的错误: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near at line 1 2:…

VD:生成a2l文件

目录 前言Simulink合并地址 ASAP2 editor 前言 我之前的方法都是通过Simulink模型生成代码的过程中顺便就把a2l文件生成出来了&#xff0c;这时的a2l文件还没有地址&#xff0c;所以紧接着会去通过elf文件更新地址&#xff0c;一直以为这是固定的流程和方法&#xff0c;今天无…

Navicat Premium 数据可视化

工作区&#xff0c;数据源以及图表 数据可视化是使用可视化组件&#xff08;例如图表&#xff0c;图形和地图&#xff09;的信息和数据的图形表示。 数据可视化工具提供了一种可访问的方式&#xff0c;用于查看和理解数据中的趋势&#xff0c;异常值和其他模式。 在Navicat中&…

设置 Git 默认推送不需要输入账号和密码【Ubuntu、SSH】

如何设置 Git 默认推送不需要输入账号和密码 在使用 Git 管理代码时&#xff0c;许多开发者会遇到每次推送&#xff08;push&#xff09;或拉取&#xff08;fetch&#xff09;代码时都需要输入 GitHub 或 GitLab 等远程仓库的账号和密码的情况。虽然设置了用户名和电子邮件信息…

TCP Window Full是怎么来的

wireshark查看包时&#xff0c;会看到TCP Window Full&#xff0c;总结下它的特点&#xff1a; 1. Sender会显示 TCP Window Full 2. “Sender已发出&#xff0c;但&#xff0c;Receiver尚未ack的字节”&#xff0c;即Sender的 bytes in flights 3. Sender的 bytes in fligh…

PyTorch框架——基于WebUI:Gradio深度学习ShuffleNetv2神经网络蔬菜图像识别分类系统

第一步&#xff1a;准备数据 蔬菜数据集&#xff0c;英文为Vegetable。 train 目录下有15000 张图片。 共十五种植物的幼苗图片集&#xff0c;分别为classes [Bean, Bitter_Gourd, Bottle_Gourd, Brinjal, Broccoli, Cabbage, Capsicum, Carrot, Cauliflower, Cucumber, Pa…