Обратите внимание на другие наши ресурсы:
Youtube | Telegram | VK | Rutube | Dzen | Boosty
Сортировка подсчетом — это алгоритм, основная идея которого - сортировка последовательности путём подсчёта количества вхождений каждого элемента в ней.
Сортировка подсчетом используется в тех случаях, когда элементы имеют ограниченный диапазон значений. Алгоритм хорошо подходит для сортировки числовых данных в таких областях, как:
Принцип работы упрощённой сортировки подсчётом можно разделить на несколько шагов:
Для каждого элемента исходной последовательности создаётся счётчик, который отслеживает количество вхождений этого элемента. Индексы массива счётчиков соответствуют значениям элементов.
Проходя по каждому элементу исходной последовательности, мы увеличиваем счётчик его количества.
После подсчёта всех элементов мы формируем отсортированную последовательность, добавляя элемент в результирующую последовательность столько раз, сколько раз он встречается в исходной последовательности.
Вычислительная сложность алгоритма включает в себя:
Таким образом, общая вычислительная сложность — , где — количество элементов, — диапазон значений.
Алгоритм сортировки подсчетом состоит из нескольких этапов, каждый из которых имеет определенную вычислительную сложность:
Инициализация массива счётчиков:
Подсчёт количества каждого элемента:
Заполнение отсортированной последовательности:
Таким образом, итоговая сложность:
где — количество элементов, а — диапазон значений чисел.
Сортировка подсчетом требует дополнительной памяти для массива подсчета и вспомогательного массива для восстановления отсортированных данных:
Общая пространственная сложность — .
Рассмотрим, сколько памяти занимает алгоритм:
Массив счётчиков:
Выходной массив:
Таким образом, общая пространственная сложность будет:
где — количество элементов, а — диапазон значений.
Пусть - исходная последовательность, - массив, в котором мы будем подсчитывать количество вхождений элементов из .
Шаги алгоритма сортировки подсчётами:
Поcчитать количество каждого уникального элемента во входной последовательности.
Пусть - значение из . Увеличиваем на за каждое встреченное значение .
Перебираем все элементы массива , элемент под индексом - это количество значений в исходной последовательности.
, что означает, что в исходной последовательности отсутствует значение .
, следовательно в исходной последовательности присутствует значение , поэтому в итоговую последовательность добавляем столько раз, сколько указано в массиве счетчиков (один раз).
, потому что значение отсутствует в исходной последовательности.
, так как в исходной последовательности было два элемента с значением . Добавляем их в итоговую последовательность.
, добавляем в итоговую последовательность значение .
, в исходном массиве была цифра , поэтому в итоговую последовательность добавляем значение один раз.
В итоге получаем отсортированную последовательность .
Сортировка подсчётом для неотрицательных чисел предполагает, что все элементы последовательности имеют значения больше или равные нулю. Алгоритм подсчитывает количество каждого числа, а затем по этим данным восстанавливает отсортированную последовательность. Этот вариант работает эффективно для последовательности с малым диапазоном значений.
функция сортировка_подсчетом(последовательность):
если длина(последовательность) < 2:
вернуть последовательность
макс_значение = максимум(последовательность)
счетчик = [0] * (макс_значение + 1)
для каждого элемент в последовательность:
счетчик[элемент] += 1
результат = []
для индекс, количество в перечислить(счетчик):
добавить_в_результат(результат, [индекс] * количество)
вернуть результат
Аннотация:
counting_sort - функция сортировки подсчётом
sequence - исходная последовательность
count - массив для подсчёта элементов
maxValue - максимальный элемент из исходной последовательности
num - число исходной последовательность
result - отсортированная исходная последовательность
cnt - число вхождений числа в исходную последовательность
i - элемент исходной последовательности
def counting_sort(sequence):
if len(sequence) < 2:
return sequence
max_value = max(sequence)
count = [0] * (max_value + 1)
for num in sequence:
count[num] += 1
result = []
for i, cnt in enumerate(count):
result.extend([i] * cnt)
return result
Аннотация:
typedef - переопределение имени типа данных
countSort - функция сортировки подсчётом
seq - исходная последовательность
c - массив для подсчёта элементов
res - отсортированная последовательность
typedef vector<int> vi;
vi countSort(vi seq) {
vi c(*max_element(seq.begin(), seq.end()) + 1, 0);
for (int i : seq) c[i] += 1;
vi res;
for (int i = 0; i < c.size(); ++i) {
while (c[i] > 0) {
res.push_back(i);
c[i]--;
}
}
return res;
}
Аннотация:
template - шаблон функции
typename T - шаблонный тип данных параметра
countingSort - функция сортировки подсчётом
sequence - исходная последовательность
maxValue - максимальный элемент из исходной последовательности
count - массив для подсчёта элементов
result - отсортированная последовательность
index - текущий элемент из массива для подсчёта элементов
template <typename T>
vector<T> countingSort(vector<T> sequence) {
if (sequence.size() < 2) return sequence;
T maxValue = *max_element(sequence.begin(), sequence.end());
vector<int> count(maxValue + 1, 0);
for (T data : sequence) count[data] += 1;
vector<T> result;
for (T index = 0; index < count.size(); index++)
while (count[index]-- > 0) result.push_back(index);
return result;
}
Сортировка подсчётом для отрицательных чисел требует некоторых дополнительных шагов, поскольку стандартная реализация подсчёта подразумевает использование положительных индексов массива. Для обработки отрицательных чисел необходимо предусмотреть способ корректной работы с отрицательными значениями, учитывая их индексы.
Чтобы адаптировать сортировку подсчётом для отрицательных чисел, необходимо выполнить несколько ключевых шагов:
Определить минимальное значение: первым делом нужно вычислить минимальное значение в последовательности, чтобы иметь представление о том, насколько нужно сместить индексы массива счётчиков.
Сдвиг индексов: поскольку индексы массива для счётчиков должны быть неотрицательными, мы будем смещать все значения массива на такое количество единиц, чтобы минимальное значение соответствовало индексу 0. Это позволит эффективно работать с отрицательными числами.
Корректировать индексирование: после сдвига все индексы массива счётчиков будут использоваться относительно минимального значения, корректно сопоставляя исходные отрицательные числа их новым положительным индексам.
Функция сортировкаПодсчётомСОтрицательными(последовательность):
если длина(последовательность) < 2:
вернуть последовательность
минимум = мин(последовательность)
максимум = макс(последовательность)
счётчики = массив из нулей длиной (максимум - минимум + 1)
результат = массив из нулей длиной (длина(последовательность))
Для каждого элемент в последовательность:
счётчики[элемент - минимум] += 1
Для каждого индекс от 1 до длина(счётчики):
счётчики[индекс] += счётчики[индекс - 1]
Для каждого элемент в обратном порядке последовательность:
счётчики[элемент - минимум] -= 1
результат[счётчики[элемент - минимум]] = элемент
Вернуть результат
Аннотация:
counting_sort_negative - функция сортировки подсчётами для отрицательных чисел
sequence - исходная последовательность
min_value - минимальный элемент из исходной последовательности
max_value - максимальный элемент из исходной последовательности
count - массив для подсчёта элементов
result - отсортированная исходная последовательность
i - элемент исходной последовательности
cnt - число вхождений числа в исходную последовательность
def counting_sort_negative(sequence):
if len(sequence) < 2:
return sequence
min_value = min(sequence)
max_value = max(sequence)
count = [0] * (max_value - min_value + 1)
for num in sequence:
count[num - min_value] += 1
result = []
for i, cnt in enumerate(count):
result.extend([i + min_value] * cnt)
return result
Аннотация:
typedef - переопределение имени типа данных
countSortNegative - функция сортировки подсчётами для отрицательных чисел
seq - исходная последовательность
l - минимальный элемент из исходной последовательности
h - максимальный элемент из исходной последовательности
res - отсортированная последовательность
c - массив для подсчёта элементов
typedef vector<int> vi;
vi countSortNegative(vi seq) {
int l = *min_element(seq.begin(), seq.end());
int h = *max_element(seq.begin(), seq.end());
vi c(high - low + 1, 0), res(seq.size(), 0);
for (int i : seq)
c[i - l] += 1;
for (int j = 1; j < c.size(); ++j)
c[j] += c[j - 1];
reverse(seq.begin(), seq.end());
for (int i : seq) {
c[i - l] -= 1;
res[c[i - l]] = i;
}
return res;
}
Аннотация:
template - шаблон функции
typename T - шаблонный тип данных параметра
countingSortForNegative - функция сортировки подсчётами для отрицательных чисел
sequence - исходная последовательность
low - минимальный элемент из исходной последовательности
high - максимальный элемент из исходной последовательности
count - массив для подсчёта элементов
result - отсортированная последовательность
template <typename T>
vector<T> countingSortForNegative(vector<T> sequence) {
if (sequence.size() < 2) return sequence;
int low = *min_element(sequence.begin(), sequence.end());
int high = *max_element(sequence.begin(), sequence.end());
vector<T> result(sequence.size(), 0);
vector<int> count(high - low + 1, 0);
for (T data : sequence)
count[data - low] += 1;
for (int index = 1; index < count.size(); index++)
count[index] += count[index - 1];
reverse(sequence.begin(), sequence.end());
for (T data : sequence) {
count[data - low] -= 1;
result[count[data - low]] = data;
}
return result;
}
Классическая сортировка подсчётом отличается от упрощённой тем, что она включает этап кумулятивного суммирования. Этот шаг позволяет сохранить устойчивость сортировки, то есть элементы с одинаковыми значениями остаются в том же порядке, что и в исходной последовательности. В классической версии создаётся массив счётчиков, который преобразуется в кумулятивный массив, после чего элементы из исходной последовательности вставляются в правильные позиции в результирующую последовательность. Эта версия эффективна для сортировки, где важен порядок одинаковых элементов.
При реализации сортировки подсчётом для неотрицательных чисел, важно учитывать два аспекта: устойчивость и корректную работу с диапазоном значений.
Устойчивость сортировки означает, что элементы с одинаковыми значениями сохраняют свой относительный порядок в отсортированной последовательности. Это важно в тех случаях, когда элементы содержат не только значения для сортировки, но и дополнительные данные, например, индексы или другие признаки.
Чтобы сохранить устойчивость сортировки, в последнем цикле кода (при заполнении итоговой последовательности) итерация по исходной последовательности должна происходить в обратном порядке. Это гарантирует, что если два элемента с одинаковыми значениями встретились в исходной последовательности, то в отсортированной последовательности они окажутся в том же порядке, в котором были в исходной последовательности.
Функция сортировкаПодсчётом(последовательность):
Если длина(последовательность) < 2:
Вернуть последовательность
счётчики = массив из нулей длиной (макс(последовательность) + 1)
результат = массив из нулей длиной (длина(последовательность))
Для каждого элемент в последовательность:
счётчики[элемент] += 1
Для каждого индекс от 1 до длина(счётчики):
счётчики[индекс] += счётчики[индекс - 1]
Для каждого элемент в обратном порядке последовательности:
счётчики[элемент] -= 1
результат[счётчики[элемент]] = элемент
Вернуть результат
Аннотация:
counting_sort - функция сортировки подсчётами
sequence - исходная последовательность
count - массив для подсчёта элементов
result - отсортированная исходная последовательность
def counting_sort(sequence):
if len(sequence) < 2:
return sequence
count = [0] * (max(sequence) + 1)
result = [0] * len(sequence)
for i in sequence:
count[i] += 1
for j in range(1, len(count)):
count[j] += count[j - 1]
for i in reversed(sequence):
count[i] -= 1
result[count[i]] = i
return result
Аннотация:
typedef - переопределение имени типа данных
countSort - функция сортировки подсчётами
seq - исходная последовательность
res - отсортированная исходная последовательность
c - массив для подсчёта элементов
typedef vector<int> vi;
vi countSort(vi seq) {
vi res(seq.size(), 0);
vi c(*max_element(seq.begin(), seq.end()) + 1, 0);
for (int i : seq)
c[i] += 1;
for (int j = 1; j < c.size(); j++)
c[j] += c[j - 1];
std::reverse(seq.begin(), seq.end());
for (int i : seq) {
c[i] -= 1;
res[c[i]] = i;
}
return res;
}
Аннотация:
template - шаблон функции
typename T - шаблонный тип данных параметра
countingSort - функция сортировки подсчётами
sequence - исходная последовательность
count - массив для подсчёта элементов
result - отсортированная исходная последовательность
template <typename T>
std::vector<T> countingSort(std::vector<T> sequence) {
if (sequence.size() < 2) return sequence;
std::vector<T> count(sequence.size(), 0);
std::vector<int> result(*std::max_element(sequence.begin(), sequence.end()) + 1, 0);
for (T data : sequence)
count[data] += 1;
for (int index = 1; index < count.size(); index++)
count[index] += count[index - 1];
std::reverse(sequence.begin(), sequence.end());
for (T data : sequence) {
count[data] -= 1;
result[count[data]] = data;
}
return result;
}