查找没有数据结构的中位数

时间:2015-05-21 14:56:56

标签: language-agnostic median

(我的代码是用Java编写的,但问题是不可知的;我只是在寻找算法的想法)

这就是问题所在:我做了一个方法,只是找到数据集的中位数(以数组的形式给出)。这是实施:

public static double getMedian(int[] numset) {
    ArrayList<Integer> anumset = new ArrayList<Integer>();
    for(int num : numset) {
        anumset.add(num);
    }
    anumset.sort(null);

    if(anumset.size() % 2 == 0) {
        return anumset.get(anumset.size() / 2);
    } else {
        return (anumset.get(anumset.size() / 2)
                   + anumset.get((anumset.size() / 2) + 1)) / 2;
    }
}

我去的学校的老师然后挑战我写一个方法再次找到中位数,但没有使用任何数据结构。这包括任何可以容纳多个值的东西,所以包括字符串,任何形式的数组等等。我花了很长时间试图想出一个想法,我很难过。有什么想法吗?

3 个答案:

答案 0 :(得分:5)

该任务的通常算法是Hoare的Select算法。这非常类似于快速排序,除了在快速排序中您在分区后递归排序两个一半,但对于select,您只在包含感兴趣项目的分区中执行递归调用。

例如,让我们考虑这样的输入,我们将在其中找到第四个元素:

[7,1,17,21,3,12,0,5]

我们随意使用第一个元素(7)作为我们的支点。我们最初将它拆分为(带有标记为*:

的数据透视表

[1,3,0,5,] * 7,[17,21,12]

我们正在寻找第四个元素,而7是第五个元素,所以我们然后分区(仅)左侧。我们将再次使用第一个元素作为我们的支点,使用{}来标记我们现在只是忽略的输入部分。

[0] 1 [3,5] {7,17,21,12}

1最终成为第二个元素,所以我们需要将项目分区到右边(3和5):

{0,1} 3 [5] {7,17,21,12}

使用3作为枢轴元素,我们最左边没有任何内容,右边是53是第三个元素,因此我们需要向右看。这只是一个元素,因此(5)是我们的中位数。

通过忽略未使用的一面,这降低了从排序的O(n log n)到仅O(N)的复杂性[虽然我有点滥用符号 - 在这种情况下我们是&#39;处理预期的行为,而不是最坏的情况,正如大O通常所做的那样。

如果你想确保良好的行为(以牺牲平均速度稍慢为代价),还有中位数算法的中位数。

这保证了O(N)的复杂性。

答案 1 :(得分:1)

Sort数组到位。当你正在做的时,把元素放在数组的中间。无需额外存储空间。

在Java中花费n log n时间左右。最佳时间是线性的(您必须至少检查一次每个元素,以确保您得到正确的答案)。出于教学目的,额外的复杂性降低是不值得的。

如果您无法修改阵列,则必须交换额外的时间复杂度,以避免使用与输入大小一半成比例的额外存储。 (如果您愿意接受近似值,情况并非如此。)

答案 2 :(得分:1)

一些效率不高的想法:

对于数组中的每个值,遍历数组,计算低于当前值的值的数量。如果该计数是数组长度的“一半”,则为中位数。 O(n ^ 2)(需要考虑一下如何处理中值的重复。)

您可以通过跟踪到目前为止的最小值和最大值来稍微改善性能。例如,如果您已经确定50太高而不是中位数,那么您可以跳过数组中的每个值大于或等于50的计数传递。同样,如果您已经确定25如果太低,您可以跳过每个小于或等于25的值的计数传递。

在C ++中:

    int Median(const std::vector<int> &values) {
        assert(!values.empty());
        const std::size_t half = values.size() / 2;
        int min = *std::min_element(values.begin(), values.end());
        int max = *std::max_element(values.begin(), values.end());
        for (auto candidate : values) {
            if (min <= candidate && candidate <= max) {
                const std::size_t count =
                    std::count_if(values.begin(), values.end(), [&](int x)
                                    { return x < candidate; });
                if (count == half)     return candidate;
                else if (count > half) max = candidate;
                else                   min = candidate;
            }
        }
        return min + (max - min) / 2;
    }

性能糟糕,但它不使用数据结构,也不修改输入数组。