Algorithm 如何使用平衡二叉树来解决这个难题?
这是在线挑战赛(不是现场比赛)。我不需要有人帮我解决问题,只要朝着正确的方向前进。努力学习 每个节点都有一个唯一的ID,没有两个人有相同的薪水。例如,一个人的薪水是70美元,七个人的薪水是30美元。树结构表示谁监督谁。问题是谁的下属薪水第k低 例如,我选择person#2。谁在下属中排名第二#2的下属是3、4、7、8。第二低工资为50美元,属于8人 有许多查询,因此结构必须是高效的 我考虑了这个问题,研究了数据结构。二叉树似乎是个好主意,但我需要帮助 例如,我认为理想的结构是这样的,对于person#2: 每个子节点都是#2的下级,每个左分支的薪资都低于右分支。如果我在每个节点上存储多少子节点,我可以得到第k个最低值 例如:从#2,左分支1节点,右分支3节点。所以倒数第二个-1意味着我现在想要右分支中倒数第一个 移动到#3,第一个最低点到#8,50美元是正确的 我的问题:Algorithm 如何使用平衡二叉树来解决这个难题?,algorithm,tree,binary-tree,binary-search-tree,nodes,Algorithm,Tree,Binary Tree,Binary Search Tree,Nodes,这是在线挑战赛(不是现场比赛)。我不需要有人帮我解决问题,只要朝着正确的方向前进。努力学习 每个节点都有一个唯一的ID,没有两个人有相同的薪水。例如,一个人的薪水是70美元,七个人的薪水是30美元。树结构表示谁监督谁。问题是谁的下属薪水第k低 例如,我选择person#2。谁在下属中排名第二#2的下属是3、4、7、8。第二低工资为50美元,属于8人 有许多查询,因此结构必须是高效的 我考虑了这个问题,研究了数据结构。二叉树似乎是个好主意,但我需要帮助 例如,我认为理想的结构是这样的,对于pers
这里有一个可能的解决方案。对于每个节点,我们将构造一个包含该节点所有子节点值的数组,并将其按顺序排序。我们正在寻找的结果是这种形式的词典
2(40)
/ \
/ \
7(30) 3(60)
/ \
/ \
8(50) 4(80)
一旦有了它,要查询任何节点的第i个最低工资,只需获取数组的第i个元素。执行所有查询的总时间为O(q),其中q是查询数
我们如何构造这个?假设您有一个指向根节点的指针,您可以递归地为每个子节点构造排序后的薪资。将这些值存储在结果中。制作每个孩子的数组的副本,并将每个孩子的工资插入到孩子复制的数组中。使用二进制搜索查找位置,因为每个数组都已排序。现在有了k个排序数组,您可以合并它们以获得一个排序数组。如果要合并两个阵列,可以在线性时间内完成。只需循环,每次拾取数组中较小的第一个元素
对于每个节点有2个子节点的情况,合并这两个子节点的数组是O(n)tine。因为我们使用二进制搜索,所以将每个节点的工资插入到其对应的数组中是O(log(n))。复制子数组是O(n),并且有n个节点,因此预处理的总时间是O(n^2)
总运行时间为O(n^2+q)
如果我们不能假设每个节点最多有2个子节点,该怎么办?然后要合并数组,请使用算法。这在O(nlog(k))中运行,其中k是要合并的数组数,因为每个元素从堆中弹出一次,当有k个数组时,调整堆的大小需要O(log(k))
k问题有两部分:首先,找到指定的人,然后找到第k个下属。
由于树不是按id排序的,要按id查找指定的perspn,需要遍历整个树,直到找到指定的id为止。为了加快这部分的速度,我们可以构建一个hash映射,它允许我们在O(1)时间内通过id找到person节点,并且需要O(n)空间和设置时间。
然后,为了找到第k个最低工资的下属,我们需要搜索子树。因为它不是按工资排序的,所以我们必须扫描整个子树,找到第k个最低工资。这可以使用数组或堆(将子树节点放入数组或堆)来完成。第二部分是O(m log k)时间,使用堆来保持最低的k项,其中m是子坐标数,需要O(k)空间。如果m(指定人员的下属人数)和k较小,则这应该是可以接受的。这里有一个解决方案,它使用O(n log^2 n+q log n)时间和O(n log^2 n)空间(在后一个计数中不是最好的,但考虑到限制,可能已经足够好了)
通过以下操作和某种迭代方式实现一个纯函数排序列表(作为一个扩展的二叉搜索树)
{ 1 : [10, 20, 30, 40, 60 80],
2 : [30, 50, 60, 80]
...
}
在这些操作之上,实现一个操作
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|
通过将较短列表中的元素插入较长列表中
现在做一件显而易见的事情:从叶子到根遍历员工层次结构,将每个员工的有序列表设置为其下属列表的适当合并,并回答查询
分析(草图)
每个查询都需要O(logn)时间。分析中有趣的部分与预处理有关
预处理的成本主要是调用Insert()
,特别是从Merge()
,因为还有n个其他插入。每次插入都需要O(logn)时间和O(logn)空间(以字为单位)
使预处理不至于是二次的是隐式的。每次我们合并两个列表时,随后两个列表都不会合并。由于较短的列表插入较长的列表,因此每次将键插入列表时,该列表的长度至少是先前插入该键的列表的两倍。因此,每个键最多都是lg n插入的主题,这足以建立O(n log n)个插入的总体范围,从而确定所声明的资源范围。您的方法的问题是,您弄乱了依赖关系结构。例如,在以2
为根的树中,如果我随后查询有关4
的内容,您的树会说4
没有子项,这是错误的-7
,8
应该是4
的子项。而不是在薪资排序方面。这就是第二棵树的意思
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|