跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

C++ 四叉树数据结构原理与实现

介绍四叉树(Quadtree)作为二维空间递归细分的数据结构。阐述核心原理及点、区域、物体四叉树分类。提供 C++ 完整实现代码,含节点定义、插入、细分及范围查询。分析时间与空间复杂度及应用场景,如游戏碰撞检测、地图 API。探讨四叉树在 H.265 视频编码中 CTU、CU、PU、TU 的划分流程与率失真优化策略。

星云发布于 2026/3/22更新于 2026/9/969 浏览

什么是四叉树(Quadtree)?

四叉树是一种树状数据结构,其每个内部节点恰好有四个子节点。它主要用于对二维空间进行递归细分。你可以将其类比为一维空间中的二叉树,或者是三维空间中的八叉树(Octree)。

核心原理

四叉树的基本思想是:如果一个区域内包含的数据量超过了预设的阈值(容量),就将该区域等分为四个象限,并将数据分配到相应的子象限中。

通常,这四个象限被命名为:

  1. NW (North-West):左上象限
  2. NE (North-East):右上象限
  3. SW (South-West):左下象限
  4. SE (South-East):右下象限

四叉树的类型包括:

  • 点四叉树 (Point Quadtree):用于存储二维点坐标。
  • 区域四叉树 (Region Quadtree):常用于图像处理,根据颜色一致性划分。
  • 物体四叉树 (MX-CIF Quadtree):用于存储具有形状(如矩形、圆)的物体。

为什么要使用四叉树?

想象一个包含 10,000 个单位的游戏地图。如果你想检查哪些单位发生了碰撞,最直观的方法是两两比对。

  • 朴素算法复杂度:O(n^2),即 10,000 * 10,000 = 100,000,000 次检查。
  • 四叉树算法复杂度:通过将物体划分到不同的空间区域,你只需要检查同一区域内的物体。理想情况下,复杂度可降至 O(nlog n)。

示例(c++)

#include <iostream>
#include <vector>
#include <memory>
#include <random>
#include <iomanip>

// --- 基础结构定义 ---
// 表示二维平面上的点
struct Point {
    double x, y;
    int id; // 增加 ID 方便辨认
};

// 轴对齐边界框 (Axis-Aligned Bounding Box)
struct AABB {
    double x, y, w, h; // 中心点 (x,y),半宽 w,半高 h
    // 检查一个点是否在该范围内
    {
         (p.x >= x - w && p.x <= x + w && p.y >= y - h && p.y <= y + h);
    }
    
    {
         !(other.x - other.w > x + w || other.x + other.w < x - w || other.y - other.h > y + h || other.y + other.h < y - h);
    }
};


  {
:
      CAPACITY = ; 
    AABB boundary; 
    std::vector<Point> points; 
     divided = ; 
    
    std::unique_ptr<QuadTree> nw, ne, sw, se;

    
    {
         x = boundary.x;
         y = boundary.y;
         w = boundary.w / ;
         h = boundary.h / ;
        nw = std::<QuadTree>(AABB{x - w, y + h, w, h});
        ne = std::<QuadTree>(AABB{x + w, y + h, w, h});
        sw = std::<QuadTree>(AABB{x - w, y - h, w, h});
        se = std::<QuadTree>(AABB{x + w, y - h, w, h});
        divided = ;
    }

:
    (AABB boundary) : (boundary) {}

    
    {
        
         (!boundary.(p))  ;
        
         (points.() < CAPACITY && !divided) {
            points.(p);
             ;
        }
        
         (!divided) ();
        
         (nw->(p))  ;
         (ne->(p))  ;
         (sw->(p))  ;
         (se->(p))  ;
         ;
    }

    
    {
        
         (!boundary.(range)) ;
        
         ( & p : points) {
             (range.(p)) {
                found.(p);
            }
        }
        
         (divided) {
            nw->(range, found);
            ne->(range, found);
            sw->(range, found);
            se->(range, found);
        }
    }
};


{
    
    AABB worldBoundary = {, , , };
    ;

    
    ; 
    ;
    std::cout <<  << std::endl;
     ( i = ; i < ; ++i) {
        Point p = {(rng), (rng), i};
        qt.(p);
    }

    
    
    AABB searchRange = {, , , };
    std::vector<Point> results;
    qt.(searchRange, results);

    
    std::cout <<  << std::endl;
    std::cout <<  << std::endl;
    std::cout <<  << results.() <<  << std::endl;
    std::cout <<  << std::endl;
    std::cout <<  << std::endl;
     ( & p : results) {
        std::cout << p.id <<  << std::fixed << std::() << p.x <<  << p.y << std::endl;
    }
     ;
}
bool contains(const Point& p) const
return
// 检查两个矩形是否相交
bool intersects(const AABB& other) const
return
// --- 四叉树类定义 ---
class
QuadTree
private
const
int
4
// 每个节点最多存放的点数
// 节点的空间范围
// 当前节点存放的点
bool
false
// 是否已分裂
// 四个子节点:使用智能指针自动管理内存
// 空间分裂
void subdivide()
double
double
double
2.0
double
2.0
make_unique
make_unique
make_unique
make_unique
true
public
QuadTree
boundary
// 插入点
bool insert(Point p)
// 如果点不在当前范围内,返回失败
if
contains
return
false
// 如果未满且未分裂,存入当前节点
if
size
push_back
return
true
// 超过容量,如果还没分裂则分裂
if
subdivide
// 尝试递归插入子节点
if
insert
return
true
if
insert
return
true
if
insert
return
true
if
insert
return
true
return
false
// 范围查询
void query(const AABB& range, std::vector<Point>& found)
// 如果查询范围与当前节点无交集,直接返回
if
intersects
return
// 检查当前节点的点
for
const
auto
if
contains
push_back
// 如果已分裂,递归查询子节点
if
query
query
query
query
// --- 主函数:测试 ---
int main()
// 1. 初始化四叉树:范围为 x[-200, 200], y[-200, 200]
0
0
200
200
QuadTree qt(worldBoundary)
// 2. 随机生成 50 个点并插入
std::mt19937 rng(12345)
// 固定种子以便复现
std::uniform_real_distribution<double> dist(-200.0, 200.0)
"Inserting 50 points..."
for
int
0
50
dist
dist
insert
// 3. 执行范围查询
// 查询中心在 (50, 50),宽高各为 100 的矩形区域 (w=50, h=50)
50
50
50
50
query
// 4. 输出结果
"\n--- Query Results ---"
"Search Box: Center(50,50), Width 100, Height 100"
"Found "
size
" points:"
"ID\tX\t\tY"
"---------------------------------"
for
const
auto
"\t"
setprecision
2
"\t\t"
return
0

关键操作:

  1. 插入 (Insertion)

当你向树中添加一个点时,首先检查该点是否属于当前节点的边界。

  • 如果是,且当前节点未满,则直接存入。
  • 如果已满,且尚未分裂,则调用 subdivide() 将当前区域一分为四。
  • 递归地尝试将该点存入四个子节点中。
  1. 细分 (Subdivision)

细分是四叉树动态生长的关键。它将父矩形切分为四个相等的小矩形。在 C++ 实现中,使用 std::unique_ptr 可以很好地管理内存,防止内存泄漏。

  1. 范围查询 (Range Query)

这是四叉树最有价值的地方。如果你想查找某个矩形范围内的所有物体,你不需要遍历整棵树:

  • 如果查询范围与当前节点边界不相交,直接跳过该分支。
  • 如果相交,检查当前节点的点,并递归搜索子节点。

性能分析与应用场景

  • 空间复杂度:在最坏情况下(点非常密集),空间复杂度为 O(n * h),其中 h 是树深度。
  • 时间复杂度:
    • 插入:平均 O(log n)。
    • 查询:取决于分布,通常远优于 O(n)。

常见应用:

  1. 游戏引擎:用于检测数千个子弹与敌人之间的碰撞。
  2. 地图 API:如 Google Maps,根据缩放级别(LOD)加载不同精度的地图瓦片。
  3. 图像处理:一种有损压缩技术,对于颜色相近的整块区域只记录一个大节点,而非每个像素。

H265(HEVC)中的四叉树

CTU, CU, PU 与 TU

在 HEVC 中,四叉树的应用主要体现在两个层面:编码单位划分和残差变换划分。

  • CTU (Coding Tree Unit):类似于 H.264 的宏块,但尺寸更大(通常为 64 * 64)。它是四叉树划分的根节点。
  • CU (Coding Unit):CTU 递归划分出的子叶节点。CU 是进行帧内/帧间预测模式选择的基本单位。
  • PU (Prediction Unit):在 CU 级别决定预测模式后,CU 可以进一步划分成 PU 来执行具体的预测算法(对称或非对称划分)。
  • TU (Transform Unit):CU 内部用于离散余弦变换 (DCT) 和量化的基本单位,同样采用四叉树结构(称为 RQT, Residual Quadtree)。

四叉树划分的具体流程

HEVC 的四叉树划分是一个基于率失真优化 (RDO, Rate-Distortion Optimization) 的递归搜索过程。

第一步:确定 CTU 尺寸

编码器将一帧图像分成若干个固定大小的 CTU(如 64 * 64)。

第二步:递归拆分 CU (Coding Tree Split)
  1. 评估当前块:计算当前块(例如 64 * 64)在不拆分情况下的率失真代价 RDroot。
  2. 尝试拆分:将当前块等分为 4 个子块(每个 32 * 32)。
  3. 递归向下:对每个子块重复上述过程,直到达到最小允许的 CU 尺寸(如 8 * 8)。
  4. 回溯比较:
    • 计算 4 个子块的最佳 RD 总和:
    • 比较决策:如果 RDsplit < RDroot,则保留拆分;否则,将该块作为叶节点 CU,不再拆分。
第三步:确定预测单位 (PU)

当 CU 划分确定后,每个叶节点 CU 会决定如何进行预测。

  • 帧内预测:CU 可以是对称的 2N * 2N 或 N * N。
  • 帧间预测:支持对称划分(如 2N * 2N)和非对称划分(AMP,如 2N * nU),以便更精确地匹配运动物体的边界。
第四步:残差四叉树划分 (RQT / TU Split)

预测完成后,产生的残差数据需要进行变换。

  1. 残差块(通常与 CU 同大)作为残差四叉树的根。
  2. 根据残差的能量分布,再次通过四叉树决定是否拆分为更小的 TU(例如从 32 * 32 拆到 4 * 4)。
  3. 较小的 TU 能够更好地捕捉高频细节,而较大的 TU 在平坦区域能提供更高的能量集中度。

为什么四叉树在 HEVC 中如此有效?

特性优势描述
内容自适应平坦区域(如蓝天)使用大 CU,减少头信息开销;复杂区域(如人脸)使用小 CU,提高预测精度。
灵活的变换尺寸RQT 允许变换矩阵的大小与残差特性匹配,极大提升了变换效率。
信令压缩编码器只需发送简单的'划分标志位(split_flag)',解码器即可重建整棵树。

总结

四叉树是平衡'空间精度'与'计算效率'的经典工具。在实际工程中,你可能还需要考虑以下优化:

  • 动态删除:当物体移动时,需要从旧节点删除并重新插入新节点。
  • 非均匀分布处理:如果所有点都在同一个位置,四叉树会深度退化。
  • 松散四叉树 (Loose Quadtree):为了处理边界上的物体,增加节点边界的重叠量。

目录

  1. 什么是四叉树(Quadtree)?
  2. 核心原理
  3. 为什么要使用四叉树?
  4. 示例(c++)
  5. 性能分析与应用场景
  6. H265(HEVC)中的四叉树
  7. CTU, CU, PU 与 TU
  8. 四叉树划分的具体流程
  9. 第一步:确定 CTU 尺寸
  10. 第二步:递归拆分 CU (Coding Tree Split)
  11. 第三步:确定预测单位 (PU)
  12. 第四步:残差四叉树划分 (RQT / TU Split)
  13. 为什么四叉树在 HEVC 中如此有效?
  14. 总结

更多推荐文章

查看全部
  • 具身智能与视觉:机器人如何“看懂”世界?
  • 数据结构与算法:Morris 遍历
  • Visual C++运行库安装失败修复指南
  • 大模型在安防领域的实践应用
  • Copilot助力AI原生应用:提升开发效率的5种方法
  • 眼镜店 AR 在线试戴小程序技术方案
  • 基于 FPGA 的深度强化学习框架实现超音速闭环流动控制实验
  • 大模型数据标注合规指南:从技术到法规
  • AVL 树:从概念到手撕实现的核心详解
  • MySQL JDBC 基础与使用详解
  • 渗透测试基本流程与实战指南
  • 医疗 AI 可信系统构建:向量索引与贝叶斯网络
  • LangChain4J Java 集成指南:从入门到高级应用
  • Docker 部署 MySQL 8.0:镜像拉取、配置与远程访问
  • Linux 进程信号的捕捉处理
  • 2025 年大型语言模型(LLM)15 大突破性发展趋势解析
  • OpenClaw 接入飞书机器人配置教程
  • 基于机器学习的生态组合塘强化城市污水处理厂脱氮优化
  • Layui 框架下 Unity WebGL Tab 切换黑屏问题解决方案
  • 深入理解 Token:自然语言处理中的文本最小单元

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online