如何使用时间复杂度为O(log n)的方法移除一个集合的中位数?有什么想法吗?
以下是基于TreeSet的Java解决方案:
public class SetWithMedian {
private SortedSet<Integer> s = new TreeSet<Integer>();
private Integer m = null;
public boolean contains(int e) {
return s.contains(e);
}
public Integer getMedian() {
return m;
}
public void add(int e) {
s.add(e);
updateMedian();
}
public void remove(int e) {
s.remove(e);
updateMedian();
}
private void updateMedian() {
if (s.size() == 0) {
m = null;
} else if (s.size() == 1) {
m = s.first();
} else {
SortedSet<Integer> h = s.headSet(m);
SortedSet<Integer> t = s.tailSet(m + 1);
int x = 1 - s.size() % 2;
if (h.size() < t.size() + x)
m = t.first();
else if (h.size() > t.size() + x)
m = h.last();
}
}
}
删除中位数(即“s.remove(s.getMedian())”)需要O(log n)的时间。
编辑:为了帮助理解代码,这里是类属性的不变条件:
private boolean isGood() {
if (s.isEmpty()) {
return m == null;
} else {
return s.contains(m) && s.headSet(m).size() + s.size() % 2 == s.tailSet(m).size();
}
}
人类可读的形式:
我知道一种期望时间复杂度为O(n)的随机化算法。
以下是该算法:
输入:n个数字的数组A[1...n] [不失一般性,我们可以假设n为偶数]
输出:排序后的第n/2个元素。
算法(A[1..n],k=n/2):
从1...n中随机选择一个轴点-p
将数组分成两部分:
L-具有小于等于A[p]的元素
R-具有大于A[p]的元素
如果(n/2 == |L|),则A[|L| + 1]是中位数,停止
如果(n/2 < |L|),则对(L,k)进行递归
否则,对(R,k - (|L| + 1))进行递归
复杂度:O(n)
证明是全数学的。一页长。如果您感兴趣,请与我联系。
尤达大师的随机算法,像其他任何算法一样,最小复杂度为n,期望复杂度为n(不是log n),最大复杂度为n平方,就像快速排序一样。它仍然非常好。
在实践中,“随机”枢轴选择有时可能是一个固定位置(不涉及RNG),因为初始数组元素已知足够随机(例如,独立且相同分布的随机排列或独立且相同分布的值),或者从近似或完全已知的输入值分布推导出来。
// partial_sort example
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main () {
int myints[] = {9,8,7,6,5,4,3,2,1};
vector<int> myvector (myints, myints+9);
vector<int>::iterator it;
partial_sort (myvector.begin(), myvector.begin()+5, myvector.end());
// print out content:
cout << "myvector contains:";
for (it=myvector.begin(); it!=myvector.end(); ++it)
cout << " " << *it;
cout << endl;
return 0;
}
输出: myvector 包含:1 2 3 4 5 9 8 7 6
中间的元素将是中位数。