我在一次面试中遇到了一个有趣的算法问题。我给出了我的答案,但是不确定是否有更好的想法。我欢迎大家写下自己的想法。
你有一个空集合。现在一个接一个地将元素放入集合中。我们假设所有元素都是整数且它们是不同的(根据集合的定义,我们不考虑具有相同值的两个元素)。
每当向集合中添加新元素时,就会询问集合的中位数。中位数的定义与数学中相同:排序列表中的中间元素。这里特别说明一下,当集合大小为偶数时,假设集合大小=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。
第三种解决方案是使用二叉搜索树。使用树可以避免数组的移动,但是使用二叉搜索树来查找中位数并不是很理想。因此,我改变了二叉搜索树的方式,使得左子树和右子树始终保持平衡。这意味着在任何时候,左子树和右子树要么具有相同数量的节点,要么右子树比左子树多一个节点。换句话说,确保在任何时候,根元素都是中位数。当然,这需要更改构建树的方式。技术细节类似于旋转红黑树。如果树被正确地维护,就可以确保最坏时间复杂度为O(n)。
因此,这三个算法对于集合的大小都是线性的。如果不存在亚线性算法,则可以将这三个算法视为最佳解决方案。由于它们之间的差异不大,最好的是最容易实现的第二种方法,即使用链接列表。
因此,我真正想知道的是,是否存在该问题的亚线性算法,如果存在,它会是什么样子。有什么想法吗?
Steve.