1

I need to find nth largest element in an array and currently I'm doing it the following way:

std::vector<double> buffer(sequence); // sequence is const std::vector<double>
std::nth_element(buffer.begin(), buffer.begin() + idx, buffer.end(), std::greater<double>());
nth_element = buffer[idx];

But is there any way to find the n-th largest element in an array without using an external buffer?

aob
  • 197
  • 8
  • 1
    What is the type of `sequence`, and what is `SignalType`? Why can't you use `sequence` directly? – BoBTFish Apr 07 '16 at 10:46
  • 1
    What exactly do you mean? You can use `nth_element` on any sequence that is accessible with Random Access iterators. But the sequence will be modified (partially sorted) in the process. Do you mean you want an `nth_element` that does not modify the sequence it is working on? – mindriot Apr 07 '16 at 10:48
  • I'm wondering if there is some other approach to find the nth largest element in an array? Because the sequence can be big and I don't want to spend time on copying it but also it should stay unchanged. – aob Apr 07 '16 at 10:50
  • Is array sorted or unsorted? – haccks Apr 07 '16 at 10:51
  • @haccks, no sequence is unsorted – aob Apr 07 '16 at 10:52
  • @aob. I'm not sure what you want. State in words what you want e.g. I want to find the position of the first element that is greater than – Werner Erasmus Apr 07 '16 at 11:00
  • He wants an `nth_element()` function that doesn't modify the container. – Michael Burr Apr 07 '16 at 11:03
  • @MichaelBurr. The question is really whether nth_element is the correct algorithm for his requirement... – Werner Erasmus Apr 07 '16 at 11:08
  • Is there repetition? If not you could use a set. – Jonathan Mee Apr 07 '16 at 11:22
  • It's probably not possible to do this with o(min{k, n-k}) (little-oh) space where n is the number of elements and you want to find the k-th largest element. – Niklas B. Apr 07 '16 at 11:41
  • Hm nevermind, I stand corrected: http://www.sciencedirect.com/science/article/pii/0304397595002251 However, these are not linear time it seems. And probably entirely impractical – Niklas B. Apr 07 '16 at 12:03

2 Answers2

9

You can avoid copying the entire buffer without modifying the original range by using std::partial_sort_copy. Simply copy the partially sorted range into a smaller buffer of size n, and take the last element.

If you may modify the original buffer, then you can simply use std::nth_element in place.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • Note that complexity would be `O(n log n)` versus `O(n)` for the copy + `std::nth_element`. – Jarod42 Apr 07 '16 at 12:34
  • @Jarod42 `O(s log(min(s, n)))` to be exact, where `s` is size of buffer and `n` is from "n-th". Indeed, this might only be worthwhile if the difference of `s` and `n` is great enough. – eerorika Apr 07 '16 at 12:49
0

You can use the partition function used in Quick Sort to find the nth largest elements.

The partition function will divide the original array into two parts.The first part will be smaller than A[i], The second part will be larger than A[i],partition will return i.If i is equal to n, then return A[i].If i is smaller than n,then partition the second part.If i is larger than n,then partition the first part.

It won't cost you extra buffer,and the average time cost is O(n).

Yuan Wen
  • 1,583
  • 3
  • 20
  • 38
  • 1
    This only needs to find the nth largest element,not the same as Quick Sort.You can refer to "Introduction to Algorithm" 9.2.@JonathanMee – Yuan Wen Apr 07 '16 at 11:20
  • 1
    Note that the partition function takes a random element, *that element will not necessarily be the 9th*. Your own statement: "If *i* is smaller than *n*,then partition the second part. If *i* is larger than *n*,then partition the first part." Clearly indicates that we we must look at elements more than once to find the 9th so this is *not* *O(n)*. – Jonathan Mee Apr 07 '16 at 11:27
  • @JonathanMee The algorithm described here is actually called [Quickselect](https://en.wikipedia.org/wiki/Quickselect). Since you always know which "half" to descend into, you get expected linear time. Of course you might look at elements more than once, but only an amortized constant number of times per element (again, expected value here) – Niklas B. Apr 07 '16 at 11:43
  • However this does not answer the question at all. Quickselect is probably exactly what `std::nth_element` implements, but it is in-place which OP wants to avoid – Niklas B. Apr 07 '16 at 11:44
  • @NiklasB. "Of course *you might look at elements more than once*, but only an amortized constant number of times per element (again, expected value here)" Negative Ghost Rider, what you are describing is *O(n log(n))*, not *O(n)*. – Jonathan Mee Apr 07 '16 at 11:45
  • You can read the 9.2 "Selection in expected linear time" part of "Introduction to Algorithm" .@JonathanMee – Yuan Wen Apr 07 '16 at 11:51
  • 1
    @JonathanMee Nah, the recurrence is T(n) < O(n) + T(a * n), with high probability for 0 < a < 1, which resolves to T(n) = O(n) according to the master theorem – Niklas B. Apr 07 '16 at 11:52
  • QuickSelect (the algorithm you describe) is exactly what `nth_element` does and it *does* modify the sequence. So this is not an answer to OP. – Ilya Popov Apr 07 '16 at 17:25
  • What is OP?@IlyaPopov@Niklas B. – Yuan Wen Apr 08 '16 at 00:01