为什么将点存储在二叉树中?

时间:2015-02-20 21:35:31

标签: java arrays algorithm sorting tree

此问题涵盖了On topic

中的软件算法

我正在处理来自Amazon Software Question的面试问题, 特别是"给定一组点(x,y)和一个整数" n",返回n个接近原点的点数"

以下是来自Sample Answer 的此问题的高级别伪代码回答示例 第1步:设计一个名为point的类,它有三个字段 - int x,int y,int distance
步骤2:对于给出的所有点,找到它们与原点之间的距离 第3步:将值存储在二叉树中
第4步:堆排序
步骤5:打印二叉树中的前n个值

我同意第1步和第2步,因为在面向对象设计方面有一个软件数据包Point是有意义的,封装了x,y和距离的字段。Ensapsulation

有人可以解释从3到5的设计决定吗?

这是我如何做3到5的步骤 第3步:将所有点存储在数组中 第4步:根据距离对数组进行排序(我在这里使用类似Arrays.Sort的排序 步骤5:随着数组按升序排序,我打印出前n个值

为什么该响应的作者使用更复杂的数据结构,二叉树而不是像我使用的数组那样简单?我知道二叉树是什么 - 具有两个指针的节点的分层数据结构。在他的算法中,您是否必须使用BST?

3 个答案:

答案 0 :(得分:1)

首先,我不会说Point(x, y, distance)是好的设计或封装。 distance实际上不是一个点的一部分,它可以从xy计算出来。在设计方面,我肯定会有一个函数,即来自Point的静态方法或帮助类Points

double distance(Point a, Point b)

然后针对具体问题,我实际上同意您的解决方案,将数据放入数组中,对此数组进行排序,然后首先提取N. 这个例子可能暗示的是,heapsort实际上经常使用二进制树结构数组中按照解释here进行排序:

  
    

堆通常放在一个包含完整二叉树布局的数组中。

  

当然,如果到Point的距离没有存储在Point中,出于性能原因,它必须与数组中相应的Point对象一起放置,或任何信息允许从排序的距离(引用,索引)中获取List<Pair<Long, Point>> distancesToOrigin = new ArrayList<>(); 对象,例如

Comparator<Pair<Long, Point>>

使用{{1}}

进行排序

答案 1 :(得分:0)

没有必要使用BST。但是,在需要自我排序的结构时,最好使用BST。我不认为有必要同时使用BST和heapsort(不知何故)。您可以只使用BST并检索前n个点。您还可以使用数组,对其进行排序并使用前n个点。 如果要对Point类型的数组进行排序,可以实现Comparable接口(Point会将该接口设为imolement)并重载默认方法。 您永远不必选择任何数据结构,但通过确定您的需求,您还可以轻松确定最佳结构。

答案 2 :(得分:0)

这篇文章中描述的方法比这个问题所需的方法更复杂。如您所述,按距离进行简单排序就足够了。但是,为了帮助解释您对样本答案作者试图获得的内容的困惑,可以考虑使用k-d tree来解决的 k最近邻居问题,这是一个应用空间的结构分区到kd数据集。对于二维空间,这确实是二叉树。这棵树本身就是排序的,不需要任何堆排序。&#34;

enter image description here

应该注意的是,构建 k-d树将采用O(n log n),如果你需要在结构上重复进行最近邻搜索,那么这只是值得的。如果您只需要执行一次搜索以从原点找到k个最近邻居,则可以通过简单的O(n)搜索来完成。

如何直接从Wiki构建k-d树:

  

一个向k-d树添加新点的方式与向任何其他搜索树添加元素的方式相同。首先,遍历树,从根开始并移动到左或右子,这取决于要插入的点是否在&#34;左&#34;或&#34;对&#34;分裂面的一侧。一旦到达子节点所在的节点,将新点添加为叶节点的左子节点或右子节点,同样取决于节点的分割平面的哪一侧包含新节点。 / p>      

以这种方式添加点会导致树变得不平衡,从而导致树性能下降。树性能下降的速率取决于添加的树点的空间分布,以及相对于树大小添加的点的数量。如果树变得太不平衡,则可能需要重新平衡以恢复依赖树平衡的查询的性能,例如最近邻搜索。

一旦建立了树,就可以在O(k log n)时间内找到某个点的k个最近邻居(在你的情况下是原点)。

直接来自维基:

  

在k-d树中搜索最近邻居的过程如下:

     
      
  1. 从根节点开始,算法以递归方式向下移动树,其方式与插入搜索点时相同(即,它向左或向右移动,具体取决于点是否小于或大于拆分维度中的当前节点。)
  2.   
  3. 一旦算法到达叶节点,它就会将该节点指定为当前最佳的&#34;
  4.   
  5. 算法展开树的递归,在每个节点执行以下步骤:      
        
    1. 如果当前节点比当前节点更接近,那么它将成为当前最佳节点。
    2.   
    3. 该算法检查在分割平面的另一侧是否可能存在比当前最佳点更接近搜索点的任何点。在概念上,这通过使分裂超平面与搜索点周围的超球面相交来完成,该超球面具有等于当前最近距离的半径。由于超平面都是轴对齐的,因此将其实现为简单的比较,以查看搜索点的分割坐标与当前节点之间的差异是否小于从搜索点到当前最佳的距离(总坐标)。      
          
      1. 如果超球面穿过平面,则平面另一侧可能有更近的点,因此算法必须从当前节点向下移动树的另一个分支,寻找更近的点,遵循相同的递归过程。整个搜索。
      2.   
      3. 如果超球面不与分裂平面相交,则算法继续向上走树,并消除该节点另一侧的整个分支。
      4.   
    4.   
  6.   
  7. 当算法完成根节点的此过程时,搜索就完成了。
  8.   

这是一个非常棘手的算法,我不愿意将其描述为面试问题!幸运的是,正如你在帖子中指出的那样,这里的一般情况比需要的更复杂。但我相信这种方法可能接近你的(错误的)样本答案试图描述的内容。