如何使用平衡二叉树来解决这一挑战?

时间:2015-06-26 15:35:52

标签: algorithm tree binary-tree binary-search-tree nodes

       1(70)
        / \
       /   \
    2(40)  5(10)
     / \      \
    /   \      \
 3(60)  4(80)  6(20)
         / \
        /   \
     7(30)  8(50)

这是针对在线挑战(非现场比赛)。我不需要有人为我解决,只是为了向正确的方向努力。试着学习。

每个节点都有一个唯一的ID,没有两个人有相同的工资。例如,#1人的工资为70美元,第7人的工资为30美元。树结构表示谁监督谁。问题是谁对一个人的下属的工资最低。

例如,我选择#2人。下属中谁是第二低? #2的下属是3,4,7,8。第二低的工资是属于#8的50美元。

有很多查询,所以结构必须高效。

我考虑过这个问题并研究了数据结构。二叉树似乎是一个好主意,但我需要帮助。

例如,对于#2号人物,我认为理想的结构看起来像是

    2(40)  
     / \      
    /   \      
 7(30)  3(60)
         / \
        /   \
     8(50)  4(80)

每个子节点都是#2的下属,每个左侧分支的工资都低于右边。如果我在每个节点存储多少个孩子,我可以得到最低的k。

例如:从#2开始,左分支1节点,右分支3节点。所以第二低 - 1表示我现在想要在右分支中的第一低。

移动到#3,第一个最低点到#8,50美元是正确的。

我的问题:

  1. 我认为这种做法很好吗?这是一种有效的方法吗?

  2. 我无法弄清楚如何构建这种树。我想我可以递归地制作它们。但很难弄清楚如何让所有孩子按工资分类到新树。需要一些帮助。

3 个答案:

答案 0 :(得分:2)

这是一个使用O(n log ^ 2 n + q log n)时间和O(n log ^ 2 n)空间的解决方案(后者计数不是最好的,但考虑到限制,可能还不错)。< / p>

使用以下操作和某种迭代方式实现纯函数排序列表(作为扩充二进制搜索树)。

EmptyList() -> returns the empty list
Insert(list, key) -> returns the list where |key| has been inserted into |list|
Length(list) -> returns the length of the list
Get(list, k) -> returns the element at index |k| in |list|

在这些操作之上,实现操作

Merge(list1, list2) -> returns the union of |list1| and |list2|

将较短列表的元素插入较长的列表中。

现在做一件显而易见的事情:将员工层次结构从叶子遍历到根目录,将每个员工的有序列表设置为适当的下级列表合并,并回答查询。

分析(草图)

每个查询都需要O(log n)时间。分析中有趣的部分与预处理有关。

预处理的成本主要取决于调用Insert()的成本,特别是来自Merge(),因为还有其他n个插入。每次插入需要O(log n)时间并花费O(log n)空间(以单词测量)。

使预处理不是二次方的是隐式heavy path decomposition。每次我们合并两个列表时,两个列表都不会随后合并。由于较短的列表被插入较长的列表,每次将一个键插入列表时,该列表至少是先前插入该键的列表的两倍。因此,每个密钥最多是lg n个插入的主题,这足以建立整体O(n log n)插入的边界,从而确定所声明的资源边界。

答案 1 :(得分:0)

这是一种可能的解决方案。对于每个节点,我们将构造一个包含该节点的所有子节点值的数组,并按排序顺序保存。我们正在寻找的结果是

形式的字典
{ 1 : [10, 20, 30, 40, 60 80],
  2 : [30, 50, 60, 80] 
  ...
}

一旦我们有了这个,要查询第i个最低工资的任何节点,只需获取数组的第i个元素。执行所有查询的总时间是O(q),其中q是查询的数量。

我们如何构建这个?假设您有一个指向根节点的指针,您可以递归地为每个子节点构建已排序的工资。将这些值存储在结果中。复制每个孩子的数组,并将每个孩子的薪水插入到孩子的复制数组中。使用二进制搜索来查找位置,因为每个数组都已排序。现在你有了k个排序数组,你将它们合并以获得一个排序数组。如果要合并两个数组,可以在线性时间内完成。只需循环,选择每次较小的数组的第一个元素。

对于每个节点有2个子节点的情况,合并两个子节点的数组是O(n)tine。由于我们使用二进制搜索,因此将每个节点的工资插入其相应的数组是每个节点的O(log(n))。复制children数组是O(n),并且有n个节点,因此我们有O(n ^ 2)个总预处理时间。

总运行时间为O(n ^ 2 + q)

如果我们不能假设每个节点最多有2个孩子怎么办?然后合并数组,使用this算法。这在O(nlog(k))中运行,其中k是要合并的数组的数量,因为我们每个元素从堆中弹出一次,并且当存在k个数组时,调整堆的大小需要O(log(k))。 k<=n所以我们可以将其简化为O(nlog(n))。因此总运行时间不变。

该解决方案的空间复杂度为O(n ^ 2)。

答案 2 :(得分:0)

这个问题有两个部分:第一,找到指定的人,然后找到第k个从属人。 由于树不按id排序,因此要通过id查找指定的perspn,需要遍历整个树,直到找到指定的id。为了加快这一部分,我们可以构建一个哈希映射,它允许我们在O(1)时间内通过id找到人员节点,并且需要O(n)空间和设置时间。 然后,为了找到第k个最低工资的下属,我们需要搜索子树。由于它没有按工资排序,我们必须扫描整个子树并找到第k个最低工资。这可以使用数组或堆来完成(将子树节点放入数组或堆中)。第二部分是O(m log k)时间,使用堆来保持最低的k项,其中m是子坐标的数量,并且需要O(k)空间。如果m(指定人的下属数)和k很小,这应该是可以接受的。