我是一名编程学生,对于我正在研究的项目,我要做的事情是计算int值向量的中值。我只使用STL中的sort函数和.begin()
,.end()
和.size()
等矢量成员函数来执行此操作。
我也应该确保找到矢量具有奇数个值或偶数个值的中位数。
我坚持,下面我已经包含了我的尝试。那我哪里错了?如果您愿意给我一些指导或资源以便朝着正确的方向前进,我将不胜感激。
代码:
int CalcMHWScore(const vector<int>& hWScores)
{
const int DIVISOR = 2;
double median;
sort(hWScores.begin(), hWScores.end());
if ((hWScores.size() % DIVISOR) == 0)
{
median = ((hWScores.begin() + hWScores.size()) + (hWScores.begin() + (hWScores.size() + 1))) / DIVISOR);
}
else
{
median = ((hWScores.begin() + hWScores.size()) / DIVISOR)
}
return median;
}
谢谢!
答案 0 :(得分:56)
无需对矢量进行完全排序:std::nth_element
可以做足够的工作来将中位数放在正确的位置。有关示例,请参阅我对this question的回答。
当然,如果您的老师禁止使用正确的工具来完成这项任务,那将无济于事。
答案 1 :(得分:31)
你正在做一个额外的分工,整体上使它比它需要的要复杂一点。此外,当2在上下文中实际上更有意义时,不需要创建DIVISOR。
double CalcMHWScore(vector<int> scores)
{
size_t size = scores.size();
if (size == 0)
{
return 0; // Undefined, really.
}
else
{
sort(scores.begin(), scores.end());
if (size % 2 == 0)
{
return (scores[size / 2 - 1] + scores[size / 2]) / 2;
}
else
{
return scores[size / 2];
}
}
}
答案 2 :(得分:10)
以下是一个简单的函数,它将使用输入迭代器返回一组值的中值。它不会以分配内存为代价修改原始数据集。
// Get the median of an unordered set of numbers of arbitrary
// type without modifying the underlying dataset.
template <typename It>
auto Median(It begin, It end)
{
using T = typename std::iterator_traits<It>::value_type;
std::vector<T> data(begin, end);
std::nth_element(data.begin(), data.begin() + data.size() / 2, data.end());
return data[data.size() / 2];
}
如果您想避免分配数据集副本并愿意修改基础数据集的成本,您可以改用:
// Get the median of an unordered set of numbers of arbitrary
// type (this will modify the underlying dataset).
template <typename It>
auto Median(It begin, It end)
{
const auto size = std::distance(begin, end)
std::nth_element(begin, begin + size / 2, end);
return *std::next(begin, size / 2);
}
答案 3 :(得分:4)
const int DIVISOR = 2;
不要这样做。它只会让你的代码更复杂。您可能已经阅读了关于不使用幻数的指导原则,但是数字的均匀性与奇数是一个基本属性,因此将其抽象出来并没有任何好处,但会妨碍可读性。
if ((hWScores.size() % DIVISOR) == 0)
{
median = ((hWScores.begin() + hWScores.size()) + (hWScores.begin() + (hWScores.size() + 1))) / DIVISOR);
你将一个迭代器带到向量的末尾,取另一个迭代器,它指向一个超过向量的末尾,将迭代器加在一起(这不是一个有意义的操作),然后将得到的结果除以迭代器(也没有意义)。这是一个更复杂的案例;我将首先解释如何处理奇数大小的矢量,然后将偶数大小的情况作为练习。
}
else
{
median = ((hWScores.begin() + hWScores.size()) / DIVISOR)
同样,你要划分一个迭代器。你想要做的是通过hWScores.size() / 2
元素将迭代器增加到向量的开头:
median = *(hWScores.begin() + hWScores.size() / 2);
请注意,您必须取消引用迭代器才能从中获取值。如果使用索引,它会更直接:
median = hWScores[hWScores.size() / 2];
答案 4 :(得分:4)
我在下面给出一个示例程序,该程序与Max S.的响应中的程序有些类似。为了帮助OP提升他的知识和理解,我做了一些改变。我有:
a)通过const引用将调用更改为按值调用,因为sort将要更改向量中元素的顺序,(编辑:我刚刚看到Rob Kennedy在我准备我的时候也说过这个交)
b)将size_t替换为更合适的向量<int
&gt; :: size_type(实际上是后者的方便同义词),
c)将size / 2保存到中间变量
d)如果向量为空,则抛出异常,
e)我还介绍了条件运算符(?:)。
实际上,所有这些更正都直接来自Koenig和Moo的“Accelerated C ++”第4章。
double median(vector<int> vec)
{
typedef vector<int>::size_type vec_sz;
vec_sz size = vec.size();
if (size == 0)
throw domain_error("median of an empty vector");
sort(vec.begin(), vec.end());
vec_sz mid = size/2;
return size % 2 == 0 ? (vec[mid] + vec[mid-1]) / 2 : vec[mid];
}
答案 5 :(得分:0)
我不确定你对vector的成员函数的用户有什么限制,但使用[]
或at()
的索引访问会使访问元素变得更简单:
median = hWScores.at(hWScores.size() / 2);
您也可以使用begin() + offset
之类的迭代器,就像您目前正在做的那样,但是您需要先使用size()/2
计算正确的偏移量,然后将其添加到begin()
,而不是其他四处走走。此外,您需要取消引用生成的迭代器以访问该点的实际值:
median = *(hWScores.begin() + hWScores.size()/2)
答案 6 :(得分:0)
被接受的答案使用std::sort
,它所做的工作比我们需要做的要多。使用std::nth_element
的答案不能正确处理偶数大小的情况。
与仅使用std::sort
相比,我们可以做得更好。我们不需要对向量进行完全排序即可找到中值。我们可以使用std::nth_element
查找中间元素。由于具有偶数个元素的向量的中位数是中间两个元素的平均值,因此在这种情况下,我们需要做更多的工作才能找到另一个中间元素。 std::nth_element
确保中间位置之前的所有元素都小于中间位置。它不能保证它们的顺序超出此范围,因此我们需要使用std::max_element
来找到中间元素之前的最大元素。
int CalcMHWScore(std::vector<int> hWScores) {
assert(!hWScores.empty());
const auto middleItr = hWScores.begin() + hWScores.size() / 2;
std::nth_element(hWScores.begin(), middleItr, hWScores.end());
if (hWScores.size() % 2 == 0) {
const auto leftMiddleItr = std::max_element(hWScores.begin(), middleItr);
return (*leftMiddleItr + *middleItr) / 2;
} else {
return *middleItr;
}
}
您可能要考虑返回double
,因为当向量具有偶数大小时,中位数可能只是分数。