2

我有一个未排序的数组,我需要中位数的位置。我知道有几种算法可以计算 O(n) 中给定数组的中位数,但它们都包括某种数组的重新排序,例如中位数的中位数和随机选择。

我对中位数本身不感兴趣,我只对它在数组中的位置感兴趣。

有什么办法可以在 O(n) 中做到这一点?跟踪所有交换将产生巨大的开销,因此我正在寻找另一种解决方案。

4

3 回答 3

5

假设您有一个数据数组,并且您想找到它的中位数:

double data[MAX_DATA] = ...

创建一个索引数组,并将每个索引初始化到自己的位置,如下所示:

int index[MAX_DATA];
for (int i = 0 ; i != MAX_DATA ; i++) {
    index[i] = i;
}

现在实现具有以下更改的线性中值算法:

  • 当原始算法与 比较data[i]data[j],替换为与 的data[index[i]]比较data[index[j]]
  • 当原始算法交换data[i]anddata[j]时,交换index[i]andindex[j]代替。

由于 的元素data始终保持在原位,因此修改后的算法将产生中位数在未修改数组中的位置,而不是其在数组中的位置,其中一些元素移动到不同的位置。

在 C++ 中,您可以使用指针而不是索引来实现它,并std::nth_element在指针容器上使用,如下所示:

vector<int> data = {1, 5, 2, 20, 10, 7, 9, 1000};
vector<const int*> ptr(data.size());
transform(data.begin(), data.end(), ptr.begin(), [](const int& d) {return &d;});
auto mid = next(ptr.begin(), data.size() / 2);
nth_element(ptr.begin(), mid, ptr.end(), [](const int* lhs, const int* rhs) {return *lhs < *rhs;});
ptrdiff_t pos = *mid - &data[0];
cout << pos << endl << data[pos] << endl;

这是ideone 上演示的链接

于 2013-05-28T17:34:23.410 回答
1

这是生成索引辅助数组的工作示例,并通过std::nth_element间接比较找到输入数组的中位数

#include <algorithm>
#include <string>
#include <vector>
#include <iostream>
#include <iterator>

int main()
{
   // input data, big and expensive to sort or copy
   std::string big_data[] = { "hello", "world", "I", "need", "to", "get", "the", "median", "index" };    

   auto const N = std::distance(std::begin(big_data), std::end(big_data));
   auto const M = (N - 1) / 2; // 9 elements, median is 4th element in sorted array

   // generate indices
   std::vector<int> indices;
   auto value = 0;
   std::generate_n(std::back_inserter(indices), N, [&](){ return value++; });

   // find median of input array through indirect comparison and sorting
   std::nth_element(indices.begin(), indices.begin() + M, indices.end(), [&](int lhs, int rhs){ 
       return big_data[lhs] < big_data[rhs]; 
   });
   std::cout << indices[M] << ":" << big_data[indices[M]] << "\n";

   // check, sort input array and confirm it has the same median
   std::sort(std::begin(big_data), std::end(big_data));
   std::cout << M << ":" << big_data[M] << "\n";
}

在线输出

该算法保证了O(N)复杂性,因为它是 和 的总和std::generate_nstd::nth_element两者都O(N)在它们的输入数据中。

于 2013-05-28T17:56:43.083 回答
0

有一个 O(n log n) 算法用于跟踪无限数字流的中位数。(由于您不想更改列表,因此也可以将其视为流。)该算法涉及两个堆;一个总是指向下半部分的最大数字,另一个指向上半部分的最小数字。该算法在这里解释:http ://www.ardendertat.com/2011/11/03/programming-interview-questions-13-median-of-integer-stream/ 。您可以通过最少的自定义使用相同的代码。

于 2013-05-28T17:43:35.490 回答