我有一个数组可以说a = { 1,4,5,6,2,23,4,2};
现在我必须找到数组位置的中位数从2到6(奇数总项),所以我做了什么,我已经a[1]
a[5]
arr[0]
转到arr[4]
然后我对它进行了排序并将arr[2]
写为中位数。
但是每次我从一个数组输入值到另一个数组时,我的初始数组的值保持不变。其次我已经排序了,所以这个程序花了很多**time**
。
所以我想知道是否有任何办法可以用less my computation time
的不同方式来做。
任何网站,要了解的材料,做什么以及如何做?
答案 0 :(得分:22)
使用<algorithm>
中的std::nth_element
,即O(N):
nth_element(a, a + size / 2, a + size);
median = a[size/2];
答案 1 :(得分:15)
可以在O(n)时间内找到中位数而不排序;执行此操作的算法称为selection algorithms。
答案 2 :(得分:4)
如果您在同一个阵列上进行多个查询,那么您可以使用段树。它们通常用于执行范围最小/最大和范围求和查询,但您可以将其更改为范围中位数。
具有n个间隔的集合的分段树使用O(n log n)存储,并且可以在O(n log n)时间内构建。范围查询可以在O(log n)中完成。
范围段树中位数的示例:
您从下往上构建分段树(从上到下更新):
[5]
[3] [7]
[1,2] [4] [6] [8]
1 2 3 4 5 6 7 8
节点所涵盖的指数:
[4]
[2] [6]
[0,1] [3] [5] [7]
0 1 2 3 4 5 6 7
对于范围指数为4-6的中位数的查询将沿着这条值的路径走下去:
[4]
[5]
0 1 2 3 4 5 6 7
搜索中位数,您知道查询中的总元素数(3),该范围中的中位数将是第二个元素(索引5)。所以你基本上在搜索包含该索引的第一个节点,该节点是值为[1,2]的节点(索引0,1)。
搜索范围3-6的中位数有点复杂,因为你必须搜索恰好位于同一节点的两个索引(4,5)。
[4]
[6]
[5]
0 1 2 3 4 5 6 7
答案 3 :(得分:1)
要查找少于9个元素的数组的中位数,我认为最有效的方法是使用排序算法,如插入排序。复杂性很差,但对于这样一个小数组,由于k
更好的算法(如quicksort)的复杂性,插入排序非常有效。做你自己的基准测试,但我可以告诉你,插入排序比使用shell排序或快速排序更好。
答案 4 :(得分:0)
我认为最好的方法是使用中位数算法计算数组的第k个最大元素。你可以在这里找到算法的整体概念:Median of Medians in Java,在维基百科上:http://en.wikipedia.org/wiki/Selection_algorithm#Linear_general_selection_algorithm_-_Median_of_Medians_algorithm或者只是浏览互联网。在实现期间可以进行一些一般性改进(避免在选择特定数组的中值时进行排序)。但请注意,对于少于50个元素的数组,使用插入排序比使用中位数算法的中位数更有效。
答案 5 :(得分:0)
在某些情况下,所有现有答案都有一些缺点:
std::nth_element
效率更高,但它仍然会突变子范围,因此仍然需要一个额外的数组。由于这个原因,我发布了使用std::map
并受选择排序算法启发的方法:
std::map<int, int>
的对象中。使用此对象,我们可以有效地找到长度为subrangeLength
的子范围的中位数:
double median(const std::map<int, int> &histogram, int subrangeLength)
{
const int middle{subrangeLength / 2};
int count{0};
/* We use the fact that keys in std::map are sorted, so by simply iterating
and adding up the frequencies, we can find the median. */
if (subrangeLength % 2 == 1) {
for (const auto &freq : histogram) {
count += freq.second;
/* In case where subrangeLength is odd, "middle" is the lower integer bound of
subrangeLength / 2, so as soon as we cross it, we have found the median. */
if (count > middle) {
return freq.first;
}
}
} else {
std::optional<double> medLeft;
for (const auto &freq : histogram) {
count += freq.second;
/* In case where subrangeLength is even, we need to pay attention to the case when
elements at positions middle and middle + 1 are different. */
if (count == middle) {
medLeft = freq.first;
} else if (count > middle) {
if (!medLeft) {
medLeft = freq.first;
}
return (*medLeft + freq.first) / 2.0;
}
}
}
return -1;
}
现在,当我们想获取下一个子范围的中位数时,我们只需通过降低要删除元素的频率来更新直方图,然后为新元素添加/增加(使用{{1} },这是在恒定时间中完成的)。现在,我们再次计算中值,并继续进行直到处理所有子范围。