Language agnostic 寻找无数据结构的中值

Language agnostic 寻找无数据结构的中值,language-agnostic,median,Language Agnostic,Median,(我的代码是用Java编写的,但问题是不可知的;我只是在寻找算法的想法) 所以问题来了:我提出了一种方法,只需找到数据集的中值(以数组的形式给出)。以下是实现: public static double getMedian(int[] numset) { ArrayList<Integer> anumset = new ArrayList<Integer>(); for(int num : numset) { anumset.add(num

(我的代码是用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;
    }
}
publicstaticdoublegetmedian(int[]numset){
ArrayList anumset=新的ArrayList();
for(int num:numset){
添加(num);
}
anumset.sort(空);
如果(anumset.size()%2==0){
返回anumset.get(anumset.size()/2);
}否则{
返回(anumset.get(anumset.size()/2)
+获取((anumset.size()/2)+1))/2;
}
}
然后,我所在学校的一位老师向我提出挑战,要求我编写一种方法,在不使用任何数据结构的情况下再次找到中值。这包括任何可以容纳多个值的东西,包括字符串、任何形式的数组等等。我花了很长时间试图想出一个主意,但我被难倒了。有什么想法吗?

阵列已就位。把数组中的元素当作你已经做的。无需额外存储

在Java中,这将花费
nlogn
大约一段时间。最好的时间是线性的(你必须检查每个元素至少一次,以确保你得到正确的答案)。出于教学目的,额外的复杂性降低是不值得的


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

一些效率不高的想法:

对于数组中的每个值,通过数组计算低于当前值的值的数量。如果该计数是数组长度的“一半”,则为中值。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;
    }
int中值(常数标准::向量和值){
断言(!values.empty());
const std::size\u t half=values.size()/2;
int min=*std::min_元素(values.begin(),values.end());
int max=*std::max_元素(values.begin(),values.end());
用于(自动候选:值){

if(min任务的常用算法是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
作为轴心元素,我们的左边是零,右边是
5
3
是第三个元素,所以我们需要向右看。这只是一个元素,所以(
5
)是我们的中值

通过忽略未使用的部分,这将排序的复杂性从O(n logn)降低到只有O(n)[尽管我有点滥用符号——在这种情况下,我们处理的是预期的行为,而不是最坏的情况,正如big-O通常所做的那样]

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


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

需要引用“如果没有与输入大小的一半成比例的额外存储,就没有其他方法可以找到它。”我相信我的答案确实做到了这一点(尽管速度相当慢)耸耸肩,我很高兴放松我的要求,因为我不在乎,但是,我认为你的算法是正确的,我认为这不是现代的C++东西,所以我编译时可能会搞砸了…但是当我问这个问题的时候,{ 5, 6, 6,6 }的中值是什么?我得到了1073741826。我把它翻译成了Racket,这个代码给了我同样的答案。我想它大约取消了1073741820?@Jay Kominek:Ack!我错过了一些测试用例。Bug修复。如果你发现另一个失败的例子,请告诉我。现在还不清楚这个问题是否允许对数组进行部分重新排序。注意,原始的解决方法是对数组的副本进行排序,而不是对数组本身进行排序。