【发布时间】:2009-09-07 03:57:16
【问题描述】:
我在一次采访中遇到了一个有趣的算法问题。我给出了答案,但不确定是否有更好的主意。所以我欢迎大家写一些关于他/她的想法的东西。
你有一个空集。现在元素被一个一个地放入集合中。我们假设所有元素都是整数并且它们是不同的(根据集合的定义,我们不考虑具有相同值的两个元素)。
每次向集合中添加新元素时,都会询问集合的中值。中值的定义与数学中的相同:排序列表中的中间元素。在这里,特别是当集合的大小是偶数时,假设集合的大小=2*x,中间元素是集合的第x个元素。
一个例子: 从一个空集开始, 当添加 12 时,中位数为 12, 当加 7 时,中位数为 7, 加 8 时,中位数为 8, 加 11 时,中位数为 8, 加 5 时,中位数为 8, 加 16 时,中位数为 8, ...
请注意,首先,元素被一个一个添加到集合中,第二个,我们不知道要添加的元素。
我的回答。
既然是求中位数的问题,就需要排序。最简单的解决方案是使用普通数组并保持数组排序。当一个新元素到来时,使用二分搜索找到元素的位置 (log_n) 并将该元素添加到数组中。由于它是一个普通数组,因此需要移动数组的其余部分,其时间复杂度为 n。当插入元素时,我们可以立即得到中位数,使用实例时间。
最差的时间复杂度是:log_n + n + 1。
另一种解决方案是使用链接列表。使用链表的原因是不需要移动数组。但是找到新元素的位置需要线性搜索。添加元素需要即时时间,然后我们需要通过数组的一半找到中位数,这总是需要 n/2 时间。
最差的时间复杂度是:n + 1 + n/2。
第三种解决方案是使用二叉搜索树。使用树,我们避免了移动数组。但是使用二叉搜索树来寻找中位数并不是很有吸引力。所以我改变了二叉搜索树,使左子树和右子树始终保持平衡。这意味着在任何时候,要么左子树和右子树具有相同数量的节点,要么右子树比左子树多一个节点。换句话说,确保在任何时候,根元素都是中位数。当然,这需要改变树的构建方式。技术细节类似于旋转红黑树。
如果树维护得当,保证WORST时间复杂度为O(n)。
所以这三种算法都与集合的大小成线性关系。如果不存在次线性算法,则可以认为这三种算法是最优解。由于它们之间并没有太大区别,因此最好是最容易实现的,这是第二个,使用链接列表。
所以我真正想知道的是,是否会有针对这个问题的亚线性算法,如果有,它会是什么样子。有什么想法吗?
史蒂夫。
【问题讨论】:
-
en.wikipedia.org/wiki/Self-balancing_binary_search_tree我不确定找到中位数是否有用或其复杂度低于O(n)
-
目前还不清楚这个问题到底是什么。您想要插入集合 + 查找中位数的复杂性,还是只是在集合的各种实现中查找中位数?
-
您的第一个算法只是插入排序。如果您可以在 O(log(n)+n+1) 中实现插入排序(这只是 O(n)),那么我鼓励您发布您的代码...
-
致 John Fouhy:是的,它实际上是一种插入排序。请注意,时间复杂度 O(log(n)+n+1) 仅用于添加一个元素,而不是对 n 个元素进行排序。
-
致亚当·巴特金:很抱歉让您感到困惑。我想知道是否有更好/有趣的算法来解决这个问题:从不断增长的集合中找到中位数。除此之外,其余的只是我的想法,供参考。
标签: algorithm