Пожалуйста, расскажите больше о ваших данных.
- Сколько там предметов?
- Каково ожидаемое соотношение уникальных предметов к общему количеству предметов?
- Каково распределение фактических значений ваших целых чисел? Они обычно достаточно малы, чтобы использовать простой счетный массив? Или они сгруппированы в достаточно узкие группы? И т.д.
В любом случае я предлагаю следующую идею: сортировка слиянием, измененная для подсчета дубликатов.
То есть вы работаете не с числами, а с парами (число, частота) (вы можете использовать для этого какое-нибудь умное представление с эффективным использованием памяти, например, два массива вместо массива пар и т. Д.).
Вы начинаете с [(x1,1), (x2,1), ...] и выполняете сортировку слиянием, как обычно, но когда вы объединяете два списка, которые начинаются с одинакового значения, вы помещаете значение в вывод список с их суммой совпадений. На вашем примере:
[1:1,1:1,2:1,1:1,4:1,5:1,2:1]
Split into [1:1, 1:1, 2:1] and [1:1, 4:1, 5:1, 2:1]
Recursively process them; you get [1:2, 2:1] and [1:1, 2:1, 4:1, 5:1]
Merge them: (first / second / output)
[1:2, 2:1] / [1:1, 2:1, 4:1, 5:1] / [] - we add up 1:2 and 1:1 and get 1:3
[2:1] / [2:1, 4:1, 5:1] / [1:3] - we add up 2:1 and 2:1 and get 2:2
[] / [4:1, 5:1] / [1:3, 2:2]
[1:3, 2:2, 4:1, 5:1]
Это может быть значительно улучшено с помощью некоторых хитрых трюков для первоначального сокращения массива (получения массива значений: пары вхождений, которые намного меньше, чем исходные, но сумма «вхождений» для каждого «значения») равно числу вхождений 'value' в исходном массиве). Например, разбить массив на непрерывные блоки, значения которых отличаются не более чем на 256 или 65536, и использовать небольшой массив для подсчета вхождений внутри каждого блока. На самом деле этот прием можно применить и на более поздних этапах слияния.