Первая часть вашего алгоритма - подсчет символов - это просто генерация ключей для сортировки.
Если вы знаете, что используете только алфавитные символы [A-Za-z] *, то вы можете оптимизировать свой алгоритм, уменьшив количество используемых сегментов, но это лишь незначительная настройка.
Вторая часть - просто стабильная сортировка - есть много способов сделать это - на странице в Википедии о сортировке есть хорошее резюме. Если вас интересует только персонаж, который встречается меньше всего, то описанный вами метод («Фаза 2»), вероятно, настолько эффективен, насколько вы можете.
Единственный другой способ улучшить это, если вы можете равномерно разбить ваши буквы на фиксированное количество сегментов (скажем, 16) по всему диапазону символов, а затем повторять их в каждом сегменте. Любые корзины без символов могут быть выброшены, что экономит время на этапе сканирования / сортировки. Точно так же, если у ведра есть один символ, это делается. Вы также хотите удостовериться, что вы разбили ведро только на 16, если знаете, что в нем более одного символа.
В качестве примера используется слово test (в расчете на 4 ведра и только строчные буквы:
- генерирует 4 ведра (A-G, H-M, N-T, U-Z)
- разделить слово test:
- A-G: е,
- Н-М:
- N-T: tst
- U-Z:
- перейти к другим сегментам - (A-G имеет один символ - это должно быть как минимум, чтобы мы могли остановиться
- Если бы это было не так (что касается слова "яички"), мы можем видеть, что H-M и U-Z пусты, поэтому нам нужно будет только проверить N-T (который будет содержать tsts).
- Мы создаем 4 ведра (N-O, P-Q, R-S и T).
- Разделить буквы
- и т.д.
Преимущество этого метода в том, что нам не приходилось сканировать каждую букву. Если диапазон символов одинакового размера, то оба эти метода в лучшем случае O (n), где n - длина строки (это неизбежно, так как мы всегда должны смотреть на каждый символ), хотя при построении списков символов в мой пример может сделать алгоритм так же плохо, как O (n ^ 2). Однако по мере увеличения диапазона символов, особенно для коротких строк, использование подкадров значительно повысит производительность. Для строки в юникоде вы можете использовать гибридный подход - например, разделение всех не-ascii символов на первом этапе и использование более простого метода для части ascii.