我创建了一个程序,用于查找数字列表的中位数。数字列表是动态的,可以删除和插入数字(可以输入重复的数字),在此期间,重新评估和打印新的中位数。
我使用multimap创建了这个程序,因为
1)它已被分类的好处,
2)易于插入,删除,搜索(因为多图实现二分搜索)
3)允许重复的条目。
条目数+删除数(表示为N)的约束是:0 <0。 N <= 100,000。
我编写的程序工作并打印出正确的中位数,但速度不够快。我知道unsorted_multimap比multimap快,但是unsorted_multimap的问题是我必须对它进行排序。我必须对它进行排序,因为找到你需要有一个排序列表的中位数。所以我的问题是,使用unsorted_multimap然后快速排序条目是否可行,或者这只是荒谬的?使用矢量,快速导航矢量和使用二分搜索会更快吗?或者也许我忘记了一些我甚至都没想过的神话般的解决方案。
虽然我不是C ++的新手,但我会承认,我的时间复杂技能在某种程度上是医学上的。
我越是看自己的问题,我越开始认为只使用带快速排序和二进制搜索的向量会更好,因为数据结构基本上已经实现了向量。
答案 0 :(得分:5)
我越看自己的问题,我越开始认为只使用带快速排序和二进制搜索的向量会更好,因为数据结构基本上已经实现了向量。
如果您只有很少的更新 - 请使用未排序的std :: vector + std::nth_element算法,即O(N)。您不需要完全排序,即O(N * ln(N))。
#include <algorithm>
#include <iterator>
#include <iostream>
#include <ostream>
#include <vector>
using namespace std;
template<typename RandomAccessIterator>
RandomAccessIterator median(RandomAccessIterator first,RandomAccessIterator last)
{
RandomAccessIterator m = first + distance(first,last)/2; // handle even middle if needed
nth_element(first,m,last);
return m;
}
int main()
{
vector<int> values = {5,1,2,4,3};
cout << *median(begin(values),end(values)) << endl;
}
输出是:
3
如果您有许多更新,只从中间删除 - 请使用两个堆comocomocomocomo suggests。如果您使用fibonacci_heap - 那么您也可以从仲裁位置移除O(N)(如果没有句柄)。
如果您有许多更新并需要从仲裁地点删除O(ln(N)) - 那么请使用两个多重集合作为ipc suggests。
答案 1 :(得分:3)
如果你的目的是跟踪动态的中位数,当插入/删除元素时,你应该使用最小堆和最大堆。每一个都包含一半的元素......几天前有一个相关的问题:How to implement a Median-heap
但是,如果您需要搜索特定值以删除元素,则仍需要某种地图。
你说它很慢。每次需要中位数时,您是否从地图的开头迭代到第(N / 2)个元素?你不需要。您可以通过始终保持指向它的迭代器以及小于该值的元素数量的计数器来跟踪中值。每次插入/删除时,将new / old元素与中位数进行比较,并更新迭代器和计数器。
另一种看待它的方式是两个包含每个元素一半的多重图。一个保持元素小于中位数(或相等),另一个保持较大的元素。堆更有效地做到这一点,但他们不支持搜索。
如果您只需要几次中位数,则可以使用“选择”算法。它在Sedgewick的书中有所描述。平均需要O(n)时间。它类似于快速排序但不完全排序。它只是用随机枢轴对阵列进行分区,直到最终它在一侧“选择”较小的m个元素(m =(n + 1)/ 2)。然后你搜索那些m元素中最大的元素,这就是中位数。
答案 2 :(得分:2)
以下是在每次更新O(log N)
中实现该方法的方法:
template <typename T>
class median_set {
public:
std::multiset<T> below, above;
// O(log N)
void rebalance()
{
int diff = above.size() - below.size();
if (diff > 0) {
below.insert(*above.begin());
above.erase(above.begin());
} else if (diff < -1) {
above.insert(*below.rbegin());
below.erase(below.find(*below.rbegin()));
}
}
public:
// O(1)
bool empty() const { return below.empty() && above.empty(); }
// O(1)
T const& median() const
{
assert(!empty());
return *below.rbegin();
}
// O(log N)
void insert(T const& value)
{
if (!empty() && value > median())
above.insert(value);
else
below.insert(value);
rebalance();
}
// O(log N)
void erase(T const& value)
{
if (value > median())
above.erase(above.find(value));
else
below.erase(below.find(value));
rebalance();
}
};
这个想法如下:
您无法使用priority_queue
,因为它们不允许您删除一个项目。
答案 3 :(得分:2)
Can any one help me what is Space and Time complexity of my following C# program with details.
//Passing Integer array to Find Extreme from that Integer Array
public int extreme(int[] A)
{
int N = A.Length;
if (N == 0)
{
return -1;
}
else
{
int average = CalculateAverage(A);
return FindExtremes(A, average);
}
}
// Calaculate Average of integerArray
private int CalculateAverage(int[] integerArray)
{
int sum = 0;
foreach (int value in integerArray)
{
sum += value;
}
return Convert.ToInt32(sum / integerArray.Length);
}
//Find Extreme from that Integer Array
private int FindExtremes(int[] integerArray, int average) {
int Index = -1; int ExtremeElement = integerArray[0];
for (int i = 0; i < integerArray.Length; i++)
{
int absolute = Math.Abs(integerArray[i] - average);
if (absolute > ExtremeElement)
{
ExtremeElement = integerArray[i];
Index = i;
}
}
return Index;
}
答案 4 :(得分:1)
使用矢量几乎肯定会更好。可能在中值计算之间维护索引的辅助向量,以便您可以批量删除它们。新增加也可以放入辅助向量,排序,然后合并。