在线性时间内找到未排序数组的中位数?

时间:2015-08-10 18:43:17

标签: java arrays algorithm performance arraylist

经过仔细的研究和思考,我决定发布这个问题,这是一个续集"至今天早些时候我提出的问题。

我做了一个算法,找到了一个ArrayList的中位数,基本上我只做了一个临时的ArrayList,然后在那个ArrayList上使用Collections.sort(),我可以轻松得到中位数。问题是,对于较大的文件需要太长时间,我正在尝试(没有运气)找到算法的实现来获得未排序的数组(或ArrayList)的中位数。

根据我阅读here,使用中位数中位数算法,然后使用QuickSelect,但我找不到一个易于理解的实际实现。

这是我的代码片段,用于查找大小为filterSize的ArrayList的中位数:

while(elements.size()-counter >= filterSize){
            for(int i = 0; i<filterSize; i++){
                tempElements.add(this.elements.get(i+counter));
                if(i==filterSize){
                    break;
                }
            }

            Collections.sort(tempElements); //Sort tempElements to find median
            outputElements.add(tempElements.get((filterSize-1)/2)); //Add median to an output ArrayList after calculating median index

            counter++;
            tempElements.clear(); //Removes all elements from the tempElements and start again
        }

基本上我试图避免在代码中完全使用Collections.sort()tempElements.clear(),因此找到一种更好的算法来查找线性时间的中位数。

感谢。

2 个答案:

答案 0 :(得分:4)

我认为基本的Quickselect算法(此链接下面的代码)很容易理解:你选择一个支点,应用Quicksort的分区功能,然后查看该支点的结束位置,相应地递归只有一半。

 function partition(list, left, right, pivotIndex)
     pivotValue := list[pivotIndex]
     swap list[pivotIndex] and list[right]  // Move pivot to end
     storeIndex := left
     for i from left to right-1
         if list[i] < pivotValue
             swap list[storeIndex] and list[i]
             increment storeIndex
     swap list[right] and list[storeIndex]  // Move pivot to its final place
     return storeIndex

  // Returns the n-th smallest element of list within left..right inclusive
  // (i.e. left <= n <= right).
  // The size of the list is not changing with each recursion.
  // Thus, n does not need to be updated with each round.
  function select(list, left, right, n)
     if left = right        // If the list contains only one element,
         return list[left]  // return that element
     pivotIndex  := ...     // select a pivotIndex between left and right,
                            // e.g., left + floor(rand() * (right - left + 1))
     pivotIndex  := partition(list, left, right, pivotIndex)
     // The pivot is in its final sorted position
     if n = pivotIndex
         return list[n]
     else if n < pivotIndex
         return select(list, left, pivotIndex - 1, n)
     else
         return select(list, pivotIndex + 1, right, n)

与中位数的中位数相比,这可以退化为O(n^2),但您可以通过随机选择枢轴来显着降低发生这种情况的几率,如评论中所述。

如果您对不完全理解的中位数中位数实施不满意,我建议您选择这样的方式。

答案 1 :(得分:1)

只是为了添加另一个答案,&#34;快速选择&#34;如果您随机选择每个支点,即确定中位数具有非常紧张的运行时间,即几乎可以确定&#34;线性时间,意味着当n变大时,随着常数c增大,获得大于cn的运行时间的概率非常快地变为0。因此,除非你担心赔率甚至比赢得彩票更有可能,但是对于所有意图和目的而言,有一个常数c,这样你就不会看到大于cn的运行时间,无论n。