From my comment originally: This is closely related to a quantity ubiquitous in academic productivity assessment, the Hirsh index, better known as the $h$-index. In short it is defined as the number of publications $h$ one has such that each of them has at least $h$ citations (the largest such $h$).
The only way your problem differs is that you would be interested not only in how many publications satisfy the criterion but also what their citation counts are, but that's a trivial modification. The data's already there, the original algorithm just drops it.
The generally implemented calculation is rather straightforward and agrees with Karolis Juodelė's answer.
Update: Depending on the size and character of your data, it may be worth exploring methods which partially sort the array by filtering data above and below a pivotal point (quicksort comes to mind). Then depending on whether there's too little or too many adjust the pivot and redo on the subset that contains it and so on. You don't need an order between elements higher than $h$, and certainly not between those lower than that. So for example, once you found all elements greater or equal to $h_1$ and there's less than $h_1$ of them, you don't need to touch that subset again, just add to it. This converts the recursion inherent to quicksort to a tail recursion and thus can be rewritten as a loop.
My Haskell is a bit rusty but this should do what I described above and seems to work. Hope it can be understood to some degree, I am happy to provide further explanation.
-- just a utility function
merge :: [a] -> [a] -> [a]
merge [] ys = ys
merge (x:xs) ys = x : merge xs ys
-- the actual implementation
topImpl :: [Int] -> [Int] -> [Int]
topImpl [] granted = granted
topImpl (x:xs) granted
| x == (1 + lGreater + lGranted) = x : merge greater granted
| x > (1 + lGreater + lGranted) = topImpl smaller (x : merge greater granted)
| otherwise = topImpl greater granted
where smaller = [y | y <- xs, y < x]
greater = [y | y <- xs, y >= x]
lGreater = length greater
lGranted = length granted
-- starting point is: top of whole array, granted is empty
top :: [Int] -> [Int]
top arr = topImpl arr []
The idea is to collect in granted
what you know will definitely participate in the result, and not sort it any further. If greater
together with x
fits, we're lucky, otherwise we need to try with a smaller subset. (The pivot x
is simply whatever happened to be the first item of the sublist that's currently considered.) Note that the significant advantage against taking largest elements one by one is that we do this on blocks of average size $remaining/2$ and don't need to sort them further.
Example:
Let's take your set [1,3,4,1,3,6]
.
x = 1
, granted = []
, greater = [3,4,1,3,6]
. Ouch, we hit a pathological case when the pivot is too small (actually so small that smaller
is empty) right in the first step. Luckily our algo is ready for that. It discards x
and tries again with greater
alone.
x = 3
, granted = []
, greater = [4,3,6]
. Together, they form an array of length 4 but we only have that limited from below by 3 so that's too much. Repeat on greater
alone.
x = 4
, granted = []
, greater = [6]
. This gives an array of 2 elements ≥ 4 each, seems we might have use for some more of them. Keep this and repeat on smaller = [3]
.
x = 3
, granted = [4,6]
, greater = []
. This together gives an array of 3 elements ≥ 3 each, so we have our solution [3,4,6]
and we can return. (Note that the permutation may vary depending on the ordering of the input, but will always contain the highest possible terms, never [3,3,6]
or [3,3,4]
for your example.)
(Btw. note that the recursion indeed just collapsed to a cycle.) The complexity is somewhat better than quicksort because of the many saved comparisons:
Best case (e.g. [2,2,1,1,1,1]): a single step, $n-1$ comparisons
Average case: $O(\log n)$ steps, $O(n)$ comparisons in total
Worst case (e.g. [1,1,1,1,1,1]): $n$ steps, $O(n^2)$ comparisons in total
There are a few needless comparisons in the code above, like calculating smaller
whether we need it or not, they can be easily removed. (I think lazy evaluation will take care of that though.)