16

I don't want get a sorted array, just nth element's value. For example, given the array

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

I'd like to be able to query

nth_element(a,2) = 1

In C++, there is a function std::nth_element that can do this. Is there an equivalent Java function?

Thanks!

CAMOBAP
  • 5,523
  • 8
  • 58
  • 93
Frost
  • 3,786
  • 5
  • 23
  • 29
  • 1
    Are you looking for a built in method? No, one does not exist in the standard library. – Matt Ball Aug 11 '11 at 01:34
  • 1
    @Justin - he's asking for the nth element if the array is sorted, but without the overhead of needing to sort the array. C++ has an STL algorithm for the equivalent: http://cplusplus.com/reference/algorithm/nth_element/ – Dawson Aug 11 '11 at 01:39
  • 3
    Here is a question that describes some possible efficient algorithms (if you want something faster than sorting the array first). They are not exactly trivial. http://stackoverflow.com/questions/251781/how-to-find-the-kth-largest-element-in-an-unsorted-array-of-length-n-in-on – mellamokb Aug 11 '11 at 01:39
  • 2
    Title has "in java" question has "a java function", why do the tags have C++? – GManNickG Aug 11 '11 at 02:19

5 Answers5

6

The Java standard library does not contain an equivalent of the C++ nth_element algorithm. The closest that you'll get would be to use Collections.sort.

Alternatively, you could try implementing your own version of this function. You could implement nth_elementby doing a standard sort and calling Collections.sort, though depending on your time requirements this may be too slow. There are many specialized algorithms for performing this sort of reordering that are called selection algorithms and the Wikipedia page on the subject has several good examples. Empirically, the fastest algorithm is called quickselect and is based on the quicksort algorithm; it runs in expected O(n) time but can degrade to O(n2) for pathologically bad inputs. There is a famous (and notoriously complex) algorithm sometimes called the median-of-medians algorithm that runs in worst-case O(n), but has a high constant factor that prevents it from being used in practice.

Hope this helps!

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
1

Here is a Java implementation of nth_element:

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");
    }
}
Mike Gashler
  • 609
  • 7
  • 12
0

Quickselect algorithm is now implemented in AlgART library: https://bitbucket.org/DanielAlievsky/algart/src/master/src/main/java/net/algart/arrays/ArraySelector.java You can just use it via Maven, as described here: http://algart.net/java/AlgART/

0

There are the corresponding methods in class java.lang.reflect.Array1, see its documentation

Example:

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

1 - not to confuse with java.sql.Array

user16320675
  • 135
  • 1
  • 3
  • 9
0

In Java I think you have to implement it, something like this:

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);
}
n0rm1e
  • 3,796
  • 2
  • 28
  • 37
  • This misses the point though. `nth_smallest_element` could be done in O(n), but your solution is at least O(n lg n). You don't have to sort the entire thing. – GManNickG Aug 11 '11 at 02:20
  • The best algorithm I can think of to get nth_smallest_element includes n * lg n - n/2 * lg n/2 comparisons in the worst case (say, looking for the 100th smallest in 200 items). I'd really want to know if there is a better way of doing that in O(n). Sorting is a bit more expensive, but it is cleaner and easier. – n0rm1e Aug 11 '11 at 05:53
  • 1
    They're called "Selection Algorithm's", Wikipedia has some info, as does [this thread](http://stackoverflow.com/questions/251781/how-to-find-the-kth-largest-element-in-an-unsorted-array-of-length-n-in-on). – GManNickG Aug 11 '11 at 06:28
  • Thanks GMan for the lead. I knew 'quick sort', but didn't know about 'quick select'. I wouldn't personally use this algorithm if I had to implement it myself and spend half a day testing it ;-) – n0rm1e Aug 11 '11 at 07:02
  • 1
    The question specifically references std::nth_element, which is the C++ STL implementation of O(n) selection algorithm. Well, I guess it is not required to use that algorithm, but in practice that is obviously the chosen implementation. – Chinasaur Jul 13 '12 at 04:15