40

我是一名编程学生,对于我正在从事的项目,我要做的事情之一是计算 int 值向量的中值。我将仅使用 STL 中的排序函数和向量成员函数(例如.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;
}
4

6 回答 6

70

不需要对向量进行完全排序:std::nth_element可以做足够的工作将中位数放在正确的位置。例如,请参阅我对这个问题的回答。

当然,如果你的老师禁止使用正确的工具来完成这项工作,那也无济于事。

于 2010-01-22T11:57:41.467 回答
34

你正在做一个额外的划分,总体上使它比它需要的更复杂一些。此外,当 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];
    }
  }
}
于 2010-01-22T03:50:59.407 回答
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];
于 2010-01-22T03:58:19.250 回答
4

我在下面给出了一个示例程序,该示例程序与 Max S. 的响应中的程序有些相似。为了帮助 OP 提高他的知识和理解,我进行了一些更改。我有:

a) 将 const 引用的调用更改为值调用,因为 sort 将要更改向量中元素的顺序,(编辑:我刚刚看到 Rob Kennedy 在我准备我的帖子时也说过这个)

b) 将 size_t 替换为更合适的向量<int>::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];
}
于 2010-01-22T07:39:34.783 回答
3

接受的答案使用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;
  }
}

您可能需要考虑返回 a double,因为当向量具有偶数大小时,中位数可能是分数。

于 2019-04-21T01:31:59.757 回答
0

我不确定您对 vector 成员函数的用户的限制是什么,但是使用[]or的索引访问at()会使访问元素更简单:

median = hWScores.at(hWScores.size() / 2);

您也可以像begin() + offset现在一样使用迭代器,但是您需要首先计算正确的偏移量size()/2并将其添加到begin(),而不是相反。您还需要取消引用生成的迭代器以访问该点的实际值:

median = *(hWScores.begin() + hWScores.size()/2)
于 2010-01-22T04:03:47.743 回答