二叉树的应用是什么?


323

我想知道二叉树的特殊应用是什么。你能举一些真实的例子吗?

Answers:


425

争论二进制树的性能是没有意义的-它们不是数据结构,而是一系列具有不同性能特征的数据结构。虽然不平衡的二叉树确实比自平衡二叉树的搜索性能差很多,但是有许多二叉树(例如二叉尝试)对其“平衡”没有任何意义。

二叉树的应用

  • 二进制搜索树 -用于许多不断输入/离开数据的搜索应用程序中,例如许多语言库中的mapset对象。
  • 二进制空间分区 -几乎在所有3D视频游戏中都使用,以确定需要渲染哪些对象。
  • 二进制尝试 -几乎在每个高带宽路由器中用于存储路由器表。
  • 哈希树 -在p2p程序和专用图像签名中使用,需要验证哈希,但整个文件不可用。
  • -用于实现高效的优先级队列,这些优先级队列又用于调度许多操作系统中的进程,路由器中的服务质量以及A * (用于AI应用程序(包括机器人技术和视频游戏)的路径查找算法)。也用于堆排序。
  • 霍夫曼编码树Chip Uni)-用于压缩算法,例如.jpeg和.mp3文件格式使用的算法。
  • GGM树 -用于加密应用程序以生成伪随机数树。
  • 语法树 -由编译器和(隐式)计算器构造以解析表达式。
  • Treap-无线网络和内存分配中使用的随机数据结构。
  • T树 -尽管大多数数据库使用某种形式的B树将数据存储在驱动器上,但是将所有(大部分)数据存储在内存中的数据库通常使用T树来这样做。

二元树比n元树更常用于搜索的原因是n元树更复杂,但通常没有实际的速度优势。

在带有m节点的(平衡)二叉树中,从一个级别移至下一个级别需要一个比较,并且存在多个log_2(m)级别,以进行总计log_2(m)比较。

相比之下,n元树将需要log_2(n)比较(使用二进制搜索)才能进入下一个级别。由于存在log_n(m)总计级别,因此搜索将需要log_2(n)*log_n(m)= log_2(m)比较总计。因此,尽管n元树比较复杂,但是在进行必要的总比较方面它们没有任何优势。

(但是,n元树在小生境中仍然有用。立即想到的例子是四叉树和其他空间划分树,其中每级仅使用两个节点划分空间将使逻辑不必要地复杂;以及许多数据库中使用的B树,其局限性不是每个级别进行多少比较,而是一次可以从硬盘驱动器加载多少个节点)


3
> Treap-无线网络和内存分配中使用的随机数据结构。它们在内存分配和无线网络中的使用情况如何?
2013年

1
有许多有用的数据结构和算法都使用了“二进制”一词,而“二进制SEARCH树”实际上就是其中之一,但这并不是所要提出的问题。普通的旧“二叉树”有什么用,没有分类的树,没有平衡的树,没有完整的树。只是一棵普通的老随机树?
迈克尔·埃里克森

4
@MichaelErickson,你读了答案吗?因为这正是我回答的问题。
BlueRaja-Danny Pflughoeft

1
我相信哈希树通常被称为梅克尔树木,至少在比特币和复仇社区,IPFS等
杜克大学

1
太糟糕了,这个答案包含很多错误。现代硬件上的n元树几乎总是比二叉树更可取。大多数命名的应用程序不使用二进制树。
斯蒂芬·埃格蒙特

290

当大多数人谈论二叉树,他们不是没有想过二进制更多的时候是搜索树,所以我会先覆盖。

实际上,不平衡的二进制搜索树仅对教育学生有关数据结构的作用而已。这是因为,除非该数据是在一个相对随机的顺序来了,树可以轻松地沦为其最坏情况下的形式,这是一个链表,因为简单的二进制树均衡。

一个很好的例子:我曾经不得不修复一些将其数据加载到二叉树中进行操作和搜索的软件。它以排序形式写出数据:

Alice
Bob
Chloe
David
Edwina
Frank

这样,当读回它时,最终得到以下树:

  Alice
 /     \
=       Bob
       /   \
      =     Chloe
           /     \
          =       David
                 /     \
                =       Edwina
                       /      \
                      =        Frank
                              /     \
                             =       =

这是简并形式。如果要在那棵树中寻找Frank,则必须先搜索所有六个节点,然后才能找到他。

当平衡它们时,二叉树对于搜索真正有用。这涉及通过子树的根节点旋转子树,以使任意两个子树之间的高度差小于或等于1。将这些名字一次以上添加到平衡树中将得到以下序列:

1.   Alice
    /     \
   =       =

 

2.   Alice
    /     \
   =       Bob
          /   \
         =     =

 

3.        Bob
        _/   \_
   Alice       Chloe
  /     \     /     \
 =       =   =       =

 

4.        Bob
        _/   \_
   Alice       Chloe
  /     \     /     \
 =       =   =       David
                    /     \
                   =       =

 

5.           Bob
        ____/   \____
   Alice             David
  /     \           /     \
 =       =     Chloe       Edwina
              /     \     /      \
             =       =   =        =

 

6.              Chloe
            ___/     \___
         Bob             Edwina
        /   \           /      \
   Alice     =      David        Frank
  /     \          /     \      /     \
 =       =        =       =    =       =

实际上,随着条目的添加,您实际上可以看到整个子树向左旋转(在第3步和第6步中),这将为您提供平衡的二叉树,其中最坏情况的查找O(log N)而不是O(N简并形式给出的。最高的NULL(=)与最低的NULL绝没有任何不同。并且,在上述最后的树,你可以只看着三个节点找到弗兰克(ChloeEdwina,最后Frank)。

当然,当您使它们平衡多向树而不是二叉树时,它们会变得更加有用。这意味着每个节点拥有一个以上的项目(从技术上讲,它们包含N个项目和N + 1个指针,二叉树是具有1个项目和2个指针的1向多路树的特例)。

使用三向树,您最终得到:

  Alice Bob Chloe
 /     |   |     \
=      =   =      David Edwina Frank
                 /     |      |     \
                =      =      =      =

通常用于维护项索引的键。我编写了针对硬件进行了优化的数据库软件,其中节点的大小恰好等于磁盘块的大小(例如512字节),并且您将尽可能多的键放入单个节点中。该指针在这种情况下,实际上是创纪录的数字成固定长度的记录直接访问文件的索引文件分开(这样的记录号X可以通过只求被发现X * record_length)。

例如,如果指针为4个字节,并且密钥大小为10,则512字节节点中的密钥数为36。即36个密钥(360字节)和37个指针(148字节),总共508字节,其中每个节点浪费4个字节。

多向键的使用引入了两阶段搜索的复杂性(多路搜索以找到正确的节点,再加上小的顺序(或线性二进制)搜索以在节点中找到正确的键),但其优势在于减少磁盘I / O可以弥补这一点。

我认为没有理由在内存结构中执行此操作,最好还是坚持使用平衡的二叉树并保持代码简单。

还要记住,当数据集很小时,O(log N)over 的优点O(N)并没有真正显现出来。如果您正在使用多向树将15个人存储在通讯录中,那可能就太过分了。当您存储过去十年来来自十万个客户的每个订单之类的东西时,优势就来了。

big-O表示法的全部要点是指出N接近无限时会发生什么。某些人可能会不同意,但如果您确定数据集将保持在一定大小以下,并且只要没有其他可用的可用方法,则使用冒泡排序甚至可以,:-)


关于二叉树的其他用途,有很多用途,例如:

  • 二进制堆,其中较高的键高于或等于较低的键,而不是位于(或低于或等于并等于)左边;
  • 哈希树,类似于哈希表;
  • 用于编译计算机语言的抽象语法树;
  • 霍夫曼树,用于压缩数据;
  • 网络流量的路由树。

鉴于我为搜索树生成了多少解释,我不愿透露其他详细信息,但是您可以根据需要对它们进行足够的研究。


28
+1为此,我们写了答案;加上向我介绍平衡的多路树木,这是我以前从未遇到过的事情。
托尼2010年

3
我不同意您的说法,即除了对学生进行教育之外,它们对于其他方面无用。即使是简单的静态数据结构,它们也非常有用。但是,这是一个非常好的书面说明的答案,因此其余所有内容均为+1。:-)
Benson

1
在现代硬件上,几乎所有树木都应该是多路的。
2011年


62

二叉树是一种树数据结构,其中每个节点最多具有两个子节点,通常区分为“左”和“右”。具有子节点的节点是父节点,子节点可能包含对其父节点的引用。在树外,通常存在对“根”节点(所有节点的祖先)的引用(如果存在)。数据结构中的任何节点都可以通过从根节点开始并反复遵循对左子节点或右子节点的引用来访问。在二叉树中,每个节点的度最大为2。

二叉树

二叉树很有用,因为如图所示,如果要在树中找到任何节点,则最多只需要查找6次。例如,如果要搜索节点24,则应从根节点开始。

  • 根的值31大于24,因此您转到左节点。
  • 左侧节点的值为15,小于24,因此您转到右侧节点。
  • 右边的节点的值为23,小于24,因此您转到右边的节点。
  • 右侧节点的值为27,该值大于24,因此您将转到左侧节点。
  • 左侧节点的值为25,该值大于24,因此您将转到左侧节点。
  • 该节点的值为24,这是我们正在寻找的关键。

该搜索如下所示: 树搜索

您可以看到在第一遍中可以排除整个树的一半节点。左子树的一半在第二个。这使得搜索非常有效。如果对40 亿个元素执行此操作,则最多只需搜索32次。因此,树中包含的元素越多,搜索的效率就越高。

删除会变得很复杂。如果节点有0或1个子节点,则只需移动一些指针以排除要删除的节点即可。但是,您无法轻松删除具有2个子节点的节点。因此,我们采取捷径。假设我们要删除节点19。

删除1

由于尝试确定向左和向右指针移动的位置并不容易,因此我们找到了一个替代它。我们转到左侧的子树,然后尽可能向右走。这为我们提供了要删除的节点的下一个最大值。

删除3

现在,我们复制18的所有内容(左右指针除外),并删除原始的18节点。

删除4


为了创建这些图像,我实现了一个AVL树,即一个自平衡树,以便在任何时间点,该树在叶节点(没有子节点的节点)之间最多具有一个差异级别。这样可以防止树倾斜,并保持最大O(log n)搜索时间,而插入和删除则需要花费更多时间。

这是显示我的AVL树如何尽可能保持紧凑和平衡的示例。

在此处输入图片说明

在排序数组中,查找仍然O(log(n))像树一样进行,但是随机插入和移除将花费O(n)而不是树的O(log(n))。一些STL容器充分利用了这些性能特征,因此插入和移除时间最多为O(log n),这非常快。其中一些容器是mapmultimapset,和multiset

可在http://ideone.com/MheW8上找到AVL树的示例代码。


5
如果要处理二进制搜索树(本身平衡良好),则只需搜索O(log n)。任意二进制树没有排序约束,而随机BST的搜索复杂度为O(log h)。
dlev 2012年

这些不是存储在相关标准容器中的种类。
小狗

12

主要应用是二叉搜索树。这些是数据结构,其中搜索,插入和删除都非常快(关于log(n)操作)


1
二进制搜索树不是应用程序,而是一种特殊类型的二进制树。
nbro

@nbro:您正在争论毫无意义的语义,它们都是表达同一件事的有效方法。请注意,此处的“应用程序”与“计算机应用程序”并不意味着同一件事
BlueRaja-Danny Pflughoeft16年

我认为该问题与实际应用程序有关,而不与二进制树的特定实现或特定类型有关。顺便说一句,发问者没有问哪个数据结构是特定的二进制树。这不是没有意义的,IMO。但是我还是同意这是模棱两可的。例如,在您的其他答案中,您提到了语法树,它是树(但不一定是二进制)数据结构在实际应用程序中的应用。根据您的推理,然后我可以列举出我所知道的所有二叉树,由于项目的数量,我们都会很高兴。
nbro


11

尚未提及的二叉树的一个有趣示例是递归评估的数学表达式。从实际的角度来看,这基本上是没有用的,但是这是思考此类表达式的一种有趣的方式。

基本上,树的每个节点都有一个本身固有的值,或者通过对子节点的值进行递归操作对其进行评估。

例如,该表达式(1+3)*2可以表示为:

    *
   / \
  +   2
 / \
1   3

为了评估表达式,我们要求父级的值。该节点依次从其子级,加号运算符和仅包含“ 2”的节点获取其值。加号运算符又从值“ 1”和“ 3”的子级中获取其值,并将它们相加,将4返回到乘法节点,该乘法节点返回8。

在某种意义上,对二叉树的使用类似于反向抛光符号,因为执行操作的顺序是相同的。另外要注意的一件事是,它不一定必须是二叉树,而只是最常用的运算符是二叉树。在最基本的层面上,这里的二叉树实际上只是一种非常简单的纯函数式编程语言。



9

我认为“纯”二叉树没有任何用处。(出于教育目的,除外)平衡的二叉树(例如红黑树AVL树)更加有用,因为它们可以保证O(logn)操作。普通的二叉树最终可能只是一个列表(或几乎是列表),在使用大量数据的应用程序中并没有真正的用处。

平衡树通常用于实现地图或集合。它们也可以用于O(nlogn)排序,即使存在更好的方法也可以。

还可以使用哈希表来搜索/插入/删除哈希表哈希表通常比二进制搜索树(是否平衡)具有更好的性能。

如果需要搜索/插入/删除和排序,则(平衡的)二进制搜索树将很有用。给定准备好的构建平衡树,排序可以就地进行(几乎忽略了递归所需的堆栈空间)。它仍然是O(nlogn),但常数因子较小,并且不需要额外的空间(新数组除外,假设必须将数据放入数组中)。另一方面,哈希表无法排序(至少不能直接排序)。

也许它们在某些复杂的算法中也很有用,但我没想到。如果我发现更多信息,我将编辑我的帖子。

fe B +树等其他树在数据库中被广泛使用


9

最常见的应用之一是以排序形式有效地存储数据,以便快速访问和搜索存储的元素。例如,std::mapstd::set在C ++标准库中。

二进制树作为数据结构可用于表达式解析器和表达式求解器的各种实现。

它还可以用于解决一些数据库问题,例如索引。

通常,二叉树是基于特定树的数据结构的一般概念,可以使用不同的属性来构造各种特定类型的二叉树。


7

在C ++ STL和许多其他语言的标准库中,例如Java和C#。二叉搜索树用于实现集合和映射。


2
实际上,在C ++中,集合/映射最常见的是基于红黑树,这是具有更多限制的二进制搜索树。
Idan K

6

二进制树的最重要应用之一是平衡的二进制搜索树,例如:

这些类型的树具有以下属性:通过在每次插入或删除节点时进行诸如旋转之类的操作,可以使左子树和右子树的高度差保持较小。

因此,树的总体高度保持为log n的顺序,并且在O(log n)时间内执行诸如搜索,插入和删除节点之类的操作。C ++的STL还以集合和映射的形式实现这些树。


5

它们可以用作对数据进行排序的快速方法。将数据插入O(log(n))的二叉搜索树中。然后遍历树以对其进行排序。


2

您的程序语法,或者其他很多事情,例如自然语言,都可以使用二叉树来解析(尽管不一定)。



2

在现代硬件上,由于不良的缓存和空间行为,二叉树几乎总是次优的。(半)平衡变体也是如此。如果找到它们,那是性能不重要(或由比较功能控制)的地方,或更可能是出于历史或无知的原因。


2
与次优相比呢?

多路树。线性搜索从一个内存访问获得的所有数据比进行新的主内存访问要快得多
Stephan Eggermont

0

使用二进制树表示AST的编译器可以使用已知的算法来解析树,例如postorder,inorder。程序员无需提出自己的算法。因为源文件的二叉树比n元树高,所以它的构建需要更多的时间。采取这样的生产方式:selstmnt:=“ if”“((” expr“)”“ stmnt” ELSE“ stmnt在二叉树中,它将具有3级节点,但是n元树将具有1级(chids)

这就是基于Unix的操作系统运行缓慢的原因。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.