【问题标题】:What's the equivalent 'nth_element' function in Java?Java中等效的“nth_element”函数是什么?
【发布时间】:2011-10-24 14:15:55
【问题描述】:

我不想得到一个排序数组,只想要第 n 个元素的值。例如,给定数组

 a = [20, 5, 1, -3] 

我希望能够查询

nth_element(a,2) = 1

在 C++ 中,有一个函数 std::nth_element 可以做到这一点。有等价的Java函数吗?

谢谢!

【问题讨论】:

  • 您在寻找内置方法吗?不,标准库中不存在。
  • @Justin - 如果数组已排序,他会要求第 n 个元素,但不需要对数组进行排序的开销。 C++ 有一个等效的 STL 算法:cplusplus.com/reference/algorithm/nth_element
  • 这是一个描述一些可能的高效算法的问题(如果您想要比先排序数组更快的算法)。它们并不完全是微不足道的。 stackoverflow.com/questions/251781/…
  • 标题有“in java”的问题有“a java function”,为什么标签有C++?

标签: java c++ algorithm stl selection


【解决方案1】:

Java 标准库不包含 C++ nth_element 算法的等效项。最接近的方法是使用Collections.sort

或者,您可以尝试实现您自己的此功能版本。您可以通过执行标准排序并调用Collections.sort 来实现nth_element,但根据您的时间要求,这可能太慢了。有许多专门的算法可以执行这种重新排序,称为选择算法the Wikipedia page on the subject 有几个很好的例子。根据经验,最快的算法称为快速选择,它基于快速排序算法;它在预期的 O(n) 时间内运行,但对于病态错误的输入可能会降级到 O(n2)。有一种著名的(而且是出了名的复杂)算法,有时称为中位数算法,该算法在最坏情况 O(n) 下运行,但具有很高的常数因子,使其无法在实践中使用。

希望这会有所帮助!

【讨论】:

    【解决方案2】:

    这里是 nth_element 的 Java 实现:

    class Nth_element
    { 
        static void nth_element_helper2(double[] arr, int beg, int end)
        {
            for(int i = beg + 1; i < end; i++)
            {
                for(int j = i; j > beg; j--)
                {
                    if(arr[j - 1] < arr[j])
                        break;
                    double t = arr[j];
                    arr[j] = arr[j - 1];
                    arr[j - 1] = t;
                }
            }
        }
    
        static void nth_element_helper(double[] arr, int beg, int end, int index)
        {
            if(beg + 4 >= end)
            {
                nth_element_helper2(arr, beg, end);
                return;
            }
            int initial_beg = beg;
            int initial_end = end;
    
            // Pick a pivot (using the median of 3 technique)
            double pivA = arr[beg];
            double pivB = arr[(beg + end) / 2];
            double pivC = arr[end - 1];
            double pivot;
            if(pivA < pivB)
            {
                if(pivB < pivC)
                    pivot = pivB;
                else if(pivA < pivC)
                    pivot = pivC;
                else
                    pivot = pivA;
            }
            else
            {
                if(pivA < pivC)
                    pivot = pivA;
                else if(pivB < pivC)
                    pivot = pivC;
                else
                    pivot = pivB;
            }
    
            // Divide the values about the pivot
            while(true)
            {
                while(beg + 1 < end && arr[beg] < pivot)
                    beg++;
                while(end > beg + 1 && arr[end - 1] > pivot)
                    end--;
                if(beg + 1 >= end)
                    break;
    
                // Swap values
                double t = arr[beg];
                arr[beg] = arr[end - 1];
                arr[end - 1] = t;
    
                beg++;
                end--;
            }
            if(arr[beg] < pivot)
                beg++;
    
            // Recurse
            if(beg == initial_beg || end == initial_end)
                throw new RuntimeException("No progress. Bad pivot");
            if(index < beg) // This is where we diverge from QuickSort. We only recurse on one of the two sides. This is what makes nth_element fast.
                nth_element_helper(arr, initial_beg, beg, index);
            else
                nth_element_helper(arr, beg, initial_end, index);
        }
    
        static double nth_element(double[] arr, int index)
        {
            nth_element_helper(arr, 0, arr.length, index);
            return arr[index];
        }
    
        public static void main(String[] args)
        {
            double[] arr = { 9, 7, 1, 5, 6, 4, 3, 2, 8, 0, 10 };
            if(nth_element(arr, 5) == 5)
                System.out.println("seems to work");
            else
                System.out.println("broken");
        }
    }
    

    【讨论】:

      【解决方案3】:

      我认为你必须在 Java 中实现它,如下所示:

      Integer nth_smallest_element(Integer[] originalArray, n) {
          if (n >= originalArray.length)
              throw new RuntimeException("Invalid index");
          // Don't pass in your array, if you don't want it to be modified. Instead add them all later.
          List<Integer> copyList = new ArrayList<Integer>();
          copyList.addAll(originalArray);
          Collections.sort(copyList);
          return copyList.get(n);
      }
      

      【讨论】:

      • 这没有抓住重点。 nth_smallest_element 可以在 O(n) 中完成,但您的解决方案至少是 O(n lg n)。您不必对整个事物进行排序。
      • 我能想到的获得 nth_smallest_element 的最佳算法包括在最坏的情况下进行 n * lg n - n/2 * lg n/2 比较(例如,在 200 个项目中寻找第 100 个最小的)。我真的很想知道在 O(n) 中是否有更好的方法。排序有点贵,但更干净、更容易。
      • 它们被称为“选择算法”,维基百科有一些信息,this thread 也是如此。
      • 感谢 GMan 的领导。我知道“快速排序”,但不知道“快速选择”。如果我必须自己实现它并花半天时间测试它,我不会亲自使用这个算法;-)
      • 问题具体引用了std::nth_element,它是O(n)选择算法的C++ STL实现。好吧,我想不需要使用该算法,但实际上这显然是选择的实现。
      【解决方案4】:

      快速选择算法现已在 AlgART 库中实现: https://bitbucket.org/DanielAlievsky/algart/src/master/src/main/java/net/algart/arrays/ArraySelector.java 您可以通过 Maven 使用它,如下所述: http://algart.net/java/AlgART/

      【讨论】:

        【解决方案5】:

        java.lang.reflect.Array类中有对应的方法1,见其documentation

        例子:

        var array = new int[] { 20, 5, 1, -3};
        var value = java.lang.reflect.Array.getInt(array, 2);
        

        1 - 不要与java.sql.Array混淆

        【讨论】:

          猜你喜欢
          • 2016-12-09
          • 2014-06-15
          • 2011-11-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多