You can achieve your complexity goal by creating a histogram of values:
public static IntStream maxValues(IntStream source, int limit) { TreeMap<Integer,Integer> m=new TreeMap<>(); source.forEachOrdered(new IntConsumer() { int size, min=Integer.MIN_VALUE; public void accept(int value) { if(value<min) return; m.merge(value, 1, Integer::sum); if(size<limit) size++; else m.compute(min=m.firstKey(), (k,count)->count==1? null: count-1); } }); if(m.size()==limit)
It creates a map from int values ββto their corresponding number of occurrences, but limits its contents to the desired number of values, therefore, its work has complexity O(log(M)) (the worst case, if there are no duplicates), and since the operation is performed once for each value, its total complexity is O(NΓlog(M)) as you wish.
You can test it with the original array as
int[] arr = {5, 3, 4, 2, 9, 1, 7, 8, 6}; maxValues(Arrays.stream(arr), 3).forEach(System.out::println);
but to check for some corner cases you can use an array containing duplicates like
int[] arr = {8, 5, 3, 4, 2, 2, 9, 1, 7, 9, 8, 6};
If you want maximum performance, replacing the box trackcard with an adequate data structure using primitive data types may be feasible, but it will be a small performance optimization, since this solution has already solved the complexity problem.
By the way, this solution works for arbitrary threads, i.e. no need to know the value of N