mirror of
https://github.com/krahets/hello-algo.git
synced 2026-07-14 16:16:06 +00:00
build
This commit is contained in:
@@ -4,7 +4,7 @@ comments: true
|
||||
|
||||
# 11.3 Сортировка пузырьком
|
||||
|
||||
<u>Сортировка пузырьком (bubble sort)</u> сортирует массив за счет непрерывного сравнения и обмена соседних элементов. Этот процесс напоминает всплытие пузырьков снизу вверх, откуда и произошло название алгоритма.
|
||||
<u>Сортировка пузырьком (bubble sort)</u> реализует сортировку путем последовательного сравнения и обмена соседних элементов. Этот процесс напоминает всплытие пузырьков снизу вверх, откуда и произошло название алгоритма.
|
||||
|
||||
Как показано на рисунке 11-4, процесс "всплытия" можно смоделировать через операцию обмена элементов: начиная от левого края массива и двигаясь вправо, мы последовательно сравниваем соседние элементы и, если "левый элемент > правый элемент", меняем их местами. После завершения прохода максимальный элемент будет перемещен в самый правый конец массива.
|
||||
|
||||
@@ -297,7 +297,7 @@ comments: true
|
||||
|
||||
## 11.3.2 Оптимизация эффективности
|
||||
|
||||
Мы замечаем, что если в каком-либо раунде "всплытия" не произошло ни одного обмена, значит, массив уже отсортирован и можно сразу вернуть результат. Поэтому можно добавить флаг `flag` для отслеживания этой ситуации и немедленного выхода.
|
||||
Если в каком-либо раунде "всплытия" не произошло ни одного обмена, значит, массив уже отсортирован и можно сразу вернуть результат. Поэтому можно добавить флаг `flag` для отслеживания этой ситуации и немедленного выхода.
|
||||
|
||||
После такой оптимизации худшая и средняя временные сложности сортировки пузырьком по-прежнему равны $O(n^2)$ ; однако если входной массив уже полностью упорядочен, достигается лучшая временная сложность $O(n)$ .
|
||||
|
||||
@@ -574,7 +574,22 @@ comments: true
|
||||
=== "Ruby"
|
||||
|
||||
```ruby title="bubble_sort.rb"
|
||||
### Пузырьковая сортировка (оптимизация флагом) ###
|
||||
### Пузырьковая сортировка ###
|
||||
def bubble_sort(nums)
|
||||
n = nums.length
|
||||
# Внешний цикл: неотсортированный диапазон [0, i]
|
||||
for i in (n - 1).downto(1)
|
||||
# Внутренний цикл: переместить максимальный элемент неотсортированного диапазона [0, i] в его правый конец
|
||||
for j in 0...i
|
||||
if nums[j] > nums[j + 1]
|
||||
# Поменять местами nums[j] и nums[j + 1]
|
||||
nums[j], nums[j + 1] = nums[j + 1], nums[j]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# ## Пузырьковая сортировка (оптимизация флагом) ###
|
||||
def bubble_sort_with_flag(nums)
|
||||
n = nums.length
|
||||
# Внешний цикл: неотсортированный диапазон [0, i]
|
||||
|
||||
@@ -6,7 +6,7 @@ comments: true
|
||||
|
||||
Рассмотренные выше алгоритмы сортировки относятся к "сортировкам на основе сравнений": они упорядочивают данные, сравнивая элементы друг с другом. Временная сложность таких алгоритмов не может быть лучше $O(n \log n)$ . Далее мы рассмотрим несколько "сортировок без сравнений", чья временная сложность может достигать линейного порядка.
|
||||
|
||||
<u>Блочная сортировка (bucket sort)</u> является типичным применением стратегии "разделяй и властвуй". Она задает несколько упорядоченных по диапазонам блоков, каждый блок соответствует некоторому диапазону значений; затем данные равномерно распределяются по блокам, внутри каждого блока выполняется сортировка, а в конце результаты блоков объединяются по порядку.
|
||||
<u>Блочная сортировка (bucket sort)</u> является типичным применением стратегии "разделяй и властвуй". Она создает набор упорядоченных по величине блоков, где каждый блок соответствует определенному диапазону данных; затем элементы равномерно распределяются по этим блокам, внутри каждого блока отдельно выполняется сортировка, а в конце результаты объединяются в порядке блоков.
|
||||
|
||||
## 11.8.1 Алгоритм
|
||||
|
||||
@@ -452,7 +452,7 @@ comments: true
|
||||
|
||||
## 11.8.3 Как добиться равномерного распределения
|
||||
|
||||
Теоретически временная сложность блочной сортировки может достигать $O(n)$ ; **ключ к этому - как можно более равномерно распределить элементы по блокам**. На практике данные часто распределены неравномерно. Например, если нужно распределить все товары на Taobao по 10 блокам цен, количество товаров дешевле 100 юаней может быть очень большим, а товаров дороже 1000 юаней - очень маленьким. Если просто разбить диапазон цен на 10 равных частей, число товаров в каждом блоке будет сильно различаться.
|
||||
Теоретически временная сложность блочной сортировки может достигать $O(n)$ ; **ключ к этому - как можно более равномерно распределить элементы по блокам**. На практике данные часто распределены неравномерно. Например, если нужно распределить все товары на маркетплейсе по 10 ценовым блокам, количество товаров дешевле 100 рублей может быть очень большим, а товаров дороже 1000 рублей - очень маленьким. Если просто разбить диапазон цен на 10 равных частей, число товаров в каждом блоке будет сильно различаться.
|
||||
|
||||
Чтобы добиться более равномерного распределения, можно сначала задать грубую линию раздела и приблизительно распределить данные по 3 блокам. **После этого блоки с большим числом товаров можно снова делить на 3 блока и продолжать процесс до тех пор, пока число элементов в каждом блоке не станет примерно одинаковым**.
|
||||
|
||||
@@ -462,7 +462,7 @@ comments: true
|
||||
|
||||
<p align="center"> Рисунок 11-14 Рекурсивное разбиение по блокам </p>
|
||||
|
||||
Если нам заранее известна вероятностная модель распределения цен товаров, **то границы каждого блока можно задавать в соответствии с этим распределением**. Важно отметить, что фактическое распределение данных не обязательно специально измерять - его можно приблизить некоторой вероятностной моделью исходя из свойств данных.
|
||||
Если нам заранее известна вероятностная модель распределения цен товаров, **то границы цен для каждого блока можно задавать в соответствии с этим распределением**. Важно отметить, что фактическое распределение данных не обязательно специально измерять - его можно приблизить некоторой вероятностной моделью исходя из свойств данных.
|
||||
|
||||
Как показано на рисунке 11-15, если предположить, что цены товаров подчиняются нормальному распределению, то можно разумно задать интервалы цен и тем самым распределить товары по блокам достаточно равномерно.
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ comments: true
|
||||
|
||||
# 11.9 Сортировка подсчетом
|
||||
|
||||
<u>Сортировка подсчетом (counting sort)</u> реализует сортировку за счет подсчета количества элементов и обычно используется для массивов целых чисел.
|
||||
<u>Сортировка подсчетом (counting sort)</u> реализует сортировку за счет подсчета количества вхождений элементов и обычно используется для массивов целых чисел.
|
||||
|
||||
## 11.9.1 Простая реализация
|
||||
|
||||
@@ -368,7 +368,7 @@ comments: true
|
||||
|
||||
!!! note "Связь между сортировкой подсчетом и блочной сортировкой"
|
||||
|
||||
Если посмотреть на сортировку подсчетом с точки зрения блочной сортировки, то каждый индекс массива `counter` можно рассматривать как отдельный блок, а процесс подсчета - как распределение элементов по соответствующим блокам. По сути, сортировка подсчетом является частным случаем блочной сортировки для целочисленных данных.
|
||||
Если посмотреть на сортировку подсчетом с точки зрения блочной сортировки, то каждый индекс массива `counter` можно рассматривать как отдельный блок, а процесс подсчета - как распределение элементов по соответствующим блокам. Иными словами, сортировка подсчетом является частным случаем блочной сортировки для целочисленных данных.
|
||||
|
||||
## 11.9.2 Полная реализация
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ comments: true
|
||||
1. Подать на вход массив и построить из него мин-кучу; в этот момент минимальный элемент будет находиться в вершине кучи.
|
||||
2. Непрерывно выполнять извлечение из кучи и по порядку записывать извлеченные элементы - так получится последовательность, отсортированная по возрастанию.
|
||||
|
||||
Хотя этот метод работоспособен, он требует дополнительного массива для хранения извлеченных элементов и потому расходует лишнюю память. На практике обычно используют более изящную реализацию.
|
||||
Хотя этот метод и работоспособен, он требует дополнительного массива для хранения извлеченных элементов и потому расходует лишнюю память. На практике обычно используют более изящную реализацию.
|
||||
|
||||
## 11.7.1 Алгоритм
|
||||
|
||||
@@ -21,7 +21,7 @@ comments: true
|
||||
|
||||
1. Подать на вход массив и построить из него макс-кучу. После этого максимальный элемент окажется в вершине кучи.
|
||||
2. Обменять элемент в вершине кучи (первый элемент) с элементом внизу кучи (последний элемент). После обмена длина кучи уменьшается на $1$ , а число уже отсортированных элементов увеличивается на $1$ .
|
||||
3. Начиная с вершины, выполнить просеивание вниз (sift down) сверху вниз. После этого свойство кучи будет восстановлено.
|
||||
3. Начиная с вершины, выполнить операцию просеивания сверху вниз. После этого свойство кучи будет восстановлено.
|
||||
4. Циклически повторять шаг `2.` и шаг `3.` . После $n - 1$ раундов массив будет полностью отсортирован.
|
||||
|
||||
!!! tip
|
||||
@@ -66,7 +66,7 @@ comments: true
|
||||
|
||||
<p align="center"> Рисунок 11-12 Шаги пирамидальной сортировки </p>
|
||||
|
||||
В реализации кода используется та же функция просеивания сверху вниз `sift_down()`, что и в главе "Куча". Важно помнить, что длина кучи уменьшается по мере извлечения максимального элемента, поэтому функции `sift_down()` нужно передавать параметр длины $n$ , чтобы указать текущую эффективную длину кучи. Код приведен ниже:
|
||||
В коде используется та же функция просеивания сверху вниз `sift_down()`, что и в главе "Куча". Важно помнить, что длина кучи уменьшается по мере извлечения максимального элемента, поэтому функции `sift_down()` нужно передавать параметр длины $n$ , чтобы указать текущую действительную длину кучи. Код приведен ниже:
|
||||
|
||||
=== "Python"
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@ icon: material/sort-ascending
|
||||
|
||||
- [11.1 Алгоритмы сортировки](sorting_algorithm.md)
|
||||
- [11.2 Сортировка выбором](selection_sort.md)
|
||||
- [11.3 Пузырьковая сортировка](bubble_sort.md)
|
||||
- [11.4 Сортировка вставкой](insertion_sort.md)
|
||||
- [11.3 Сортировка пузырьком](bubble_sort.md)
|
||||
- [11.4 Сортировка вставками](insertion_sort.md)
|
||||
- [11.5 Быстрая сортировка](quick_sort.md)
|
||||
- [11.6 Сортировка слиянием](merge_sort.md)
|
||||
- [11.7 Пирамидальная сортировка](heap_sort.md)
|
||||
|
||||
@@ -4,11 +4,11 @@ comments: true
|
||||
|
||||
# 11.4 Сортировка вставками
|
||||
|
||||
<u>Сортировка вставками (insertion sort)</u> - это простой алгоритм сортировки, принцип которого очень похож на ручное упорядочивание колоды карт.
|
||||
<u>Сортировка вставками (insertion sort)</u> - это простой алгоритм сортировки, принцип которого очень похож на ручную сортировку карт в колоде.
|
||||
|
||||
Точнее говоря, в неотсортированном диапазоне выбирается опорный элемент, после чего он поочередно сравнивается с элементами слева в уже отсортированном диапазоне и вставляется в правильную позицию.
|
||||
Точнее говоря, в неотсортированном диапазоне выбирается опорный элемент, после чего он сравнивается с элементами слева в уже отсортированном диапазоне и вставляется в правильную позицию.
|
||||
|
||||
На рисунке 11-6 показан процесс вставки элемента в массив. Пусть опорный элемент обозначен как `base` ; нам нужно сдвинуть все элементы от целевого индекса до `base` на одну позицию вправо, а затем присвоить `base` значение в целевом индексе.
|
||||
На рисунке 11-6 показан процесс вставки элемента в массив. Пусть опорный элемент обозначен как `base` ; нам нужно сдвинуть все элементы от целевого индекса до `base` на одну позицию вправо, а затем записать `base` в целевой индекс.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
@@ -19,8 +19,8 @@ comments: true
|
||||
Общий процесс сортировки вставками показан на рисунке 11-7.
|
||||
|
||||
1. В начальном состоянии отсортирован только первый элемент массива.
|
||||
2. Выбрать второй элемент массива как `base` ; после вставки в правильную позицию **первые 2 элемента массива окажутся отсортированными**.
|
||||
3. Выбрать третий элемент как `base` ; после вставки в правильную позицию **первые 3 элемента массива окажутся отсортированными**.
|
||||
2. Выбрать второй элемент массива как `base` ; после вставки в правильную позицию **первые два элемента массива окажутся отсортированными**.
|
||||
3. Выбрать третий элемент как `base` ; после вставки в правильную позицию **первые три элемента массива окажутся отсортированными**.
|
||||
4. Продолжать по аналогии; в последнем раунде в качестве `base` берется последний элемент, и после его вставки **все элементы массива будут отсортированы**.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
@@ -4,10 +4,10 @@ comments: true
|
||||
|
||||
# 11.6 Сортировка слиянием
|
||||
|
||||
<u>Сортировка слиянием (merge sort)</u> - это алгоритм сортировки на основе стратегии "разделяй и властвуй", который включает этапы "разделения" и "слияния", показанные на рисунке 11-10.
|
||||
<u>Сортировка слиянием (merge sort)</u> - это алгоритм сортировки, основанный на стратегии "разделяй и властвуй", который включает этапы "разделения" и "слияния", показанные на рисунке 11-10.
|
||||
|
||||
1. **Этап разделения**: массив рекурсивно разбивается от середины, и задача сортировки длинного массива превращается в задачи сортировки более коротких массивов.
|
||||
2. **Этап слияния**: когда длина подмассива становится равной 1, разделение завершается и начинается слияние; левые и правые короткие упорядоченные массивы непрерывно объединяются в более длинный упорядоченный массив, пока процесс не завершится.
|
||||
1. **Этап разделения**: массив рекурсивно делится пополам, и задача сортировки длинного массива превращается в задачи сортировки более коротких массивов.
|
||||
2. **Этап слияния**: когда длина подмассива становится равной 1, разделение завершается и начинается слияние; два коротких упорядоченных массива непрерывно объединяются в один более длинный упорядоченный массив, пока процесс не завершится.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
@@ -54,10 +54,10 @@ comments: true
|
||||
|
||||
<p align="center"> Рисунок 11-11 Шаги сортировки слиянием </p>
|
||||
|
||||
Нетрудно заметить, что порядок рекурсии в сортировке слиянием совпадает с порядком рекурсии при постфиксном обходе бинарного дерева.
|
||||
Нетрудно заметить, что порядок рекурсии в сортировке слиянием совпадает с порядком обхода в глубину двоичного дерева.
|
||||
|
||||
- **Постфиксный обход**: сначала рекурсивно обходится левое поддерево, затем правое поддерево, а в конце обрабатывается корневой узел.
|
||||
- **Сортировка слиянием**: сначала рекурсивно обрабатывается левый подмассив, затем правый подмассив, а в конце выполняется слияние.
|
||||
- **Обход в глубину**: сначала рекурсивно обходится левое поддерево, затем правое поддерево, а в конце обрабатывается корневой узел.
|
||||
- **Сортировка слиянием**: сначала рекурсивно разделяется левый подмассив, затем правый подмассив, а в конце выполняется слияние.
|
||||
|
||||
Реализация сортировки слиянием показана в коде ниже. Обратите внимание: в `nums` объединяемый интервал равен `[left, right]` , а соответствующий интервал в `tmp` равен `[0, right - left]` .
|
||||
|
||||
@@ -697,4 +697,4 @@ comments: true
|
||||
- **Этап разделения**: работу по разбиению списка можно реализовать с помощью "итерации" вместо "рекурсии", тем самым устранив расход памяти на стек вызовов.
|
||||
- **Этап слияния**: в связном списке добавление и удаление узлов требует только изменения ссылок (указателей), поэтому при слиянии двух коротких упорядоченных списков в один длинный упорядоченный список не нужно создавать дополнительный список.
|
||||
|
||||
Детали реализации достаточно сложны; заинтересованные читатели могут изучить соответствующие материалы самостоятельно.
|
||||
Детали реализации достаточно сложны; заинтересованные читатели могут обратиться к соответствующим материалам самостоятельно.
|
||||
|
||||
@@ -43,9 +43,9 @@ comments: true
|
||||
|
||||
После завершения разделения исходный массив разбивается на три части: левый подмассив, опорный элемент и правый подмассив; при этом выполняется условие "любой элемент левого подмассива $\leq$ опорный элемент $\leq$ любой элемент правого подмассива". Следовательно, далее нам нужно лишь отсортировать эти два подмассива.
|
||||
|
||||
!!! note "Стратегия divide and conquer в быстрой сортировке"
|
||||
!!! note "Стратегия разделяй и властвуй в быстрой сортировке"
|
||||
|
||||
По сути, разделение с опорным элементом сводит задачу сортировки длинного массива к двум задачам сортировки более коротких массивов.
|
||||
Иными словами, разделение с опорным элементом сводит задачу сортировки длинного массива к двум задачам сортировки более коротких массивов.
|
||||
|
||||
=== "Python"
|
||||
|
||||
@@ -614,11 +614,11 @@ comments: true
|
||||
|
||||
## 11.5.4 Оптимизация выбора опорного элемента
|
||||
|
||||
**На некоторых входных данных временная эффективность быстрой сортировки может ухудшаться**. Рассмотрим крайний случай: входной массив полностью отсортирован в обратном порядке. Поскольку в качестве опорного мы выбираем самый левый элемент, после разделения он будет обменян в самый правый конец массива, из-за чего длина левого подмассива станет $n - 1$ , а длина правого - $0$ . Если рекурсия будет продолжаться таким образом, то после каждого разделения один из подмассивов будет иметь длину $0$ , стратегия divide and conquer потеряет смысл, а быстрая сортировка выродится в нечто близкое к "сортировке пузырьком".
|
||||
**На некоторых входных данных временная эффективность быстрой сортировки может ухудшаться**. Рассмотрим крайний случай: входной массив полностью отсортирован в обратном порядке. Поскольку в качестве опорного мы выбираем самый левый элемент, после разделения он будет обменян в самый правый конец массива, из-за чего длина левого подмассива станет $n - 1$ , а длина правого - $0$ . Если рекурсия будет продолжаться таким образом, то после каждого разделения один из подмассивов будет иметь длину $0$ , стратегия "разделяй и властвуй" потеряет смысл, а быстрая сортировка выродится в нечто близкое к "сортировке пузырьком".
|
||||
|
||||
Чтобы по возможности избежать такого сценария, **мы можем улучшить стратегию выбора опорного элемента в процедуре разделения**. Например, можно выбирать случайный элемент массива как опорный. Однако если не повезет и каждый раз будет выбираться неудачный опорный элемент, производительность все равно останется неудовлетворительной.
|
||||
Чтобы по возможности избежать такого сценария, **можно улучшить стратегию выбора опорного элемента в процедуре разделения**. Например, можно выбирать случайный элемент массива как опорный. Однако если не повезет и каждый раз будет выбираться неудачный опорный элемент, производительность все равно останется неудовлетворительной.
|
||||
|
||||
Нужно учитывать, что языки программирования обычно генерируют "псевдослучайные числа". Если специально построить тестовый пример под такую последовательность, эффективность быстрой сортировки все равно может деградировать.
|
||||
Стоит учитывать, что языки программирования обычно генерируют псевдослучайные числа. Если специально построить тестовый пример под такую последовательность, эффективность быстрой сортировки все равно может деградировать.
|
||||
|
||||
Чтобы улучшить ситуацию, можно взять три кандидата (обычно первый, последний и средний элементы массива) и **использовать медиану этих трех значений как опорный элемент**. Благодаря этому вероятность того, что опорный элемент окажется "не слишком маленьким и не слишком большим", заметно возрастает. Конечно, можно брать и большее число кандидатов, чтобы еще сильнее повысить устойчивость алгоритма. После этого вероятность деградации временной сложности до $O(n^2)$ существенно уменьшается.
|
||||
|
||||
@@ -1059,9 +1059,20 @@ comments: true
|
||||
return right
|
||||
end
|
||||
|
||||
### Разбиение с опорными указателями (медиана трех) ###
|
||||
### Выбрать медиану из трех кандидатов ###
|
||||
def median_three(nums, left, mid, right)
|
||||
# Выбрать медиану из трех кандидатов
|
||||
_l, _m, _r = nums[left], nums[mid], nums[right]
|
||||
# m находится между l и r
|
||||
return mid if (_l <= _m && _m <= _r) || (_r <= _m && _m <= _l)
|
||||
# l находится между m и r
|
||||
return left if (_m <= _l && _l <= _r) || (_r <= _l && _l <= _m)
|
||||
return right
|
||||
end
|
||||
|
||||
# ## Разбиение с опорными указателями (медиана трех) ###
|
||||
def partition(nums, left, right)
|
||||
### Использовать nums[left] как опорный элемент
|
||||
# ## Использовать nums[left] как опорный элемент
|
||||
med = median_three(nums, left, (left + right) / 2, right)
|
||||
# Переместить медиану в крайний левый элемент массива
|
||||
nums[left], nums[med] = nums[med], nums[left]
|
||||
@@ -1353,7 +1364,27 @@ comments: true
|
||||
=== "Ruby"
|
||||
|
||||
```ruby title="quick_sort.rb"
|
||||
### Быстрая сортировка (оптимизация глубины рекурсии) ###
|
||||
### Разбиение с опорными указателями ###
|
||||
def partition(nums, left, right)
|
||||
# Использовать nums[left] как опорный элемент
|
||||
i = left
|
||||
j = right
|
||||
while i < j
|
||||
while i < j && nums[j] >= nums[left]
|
||||
j -= 1 # Идти справа налево в поисках первого элемента меньше опорного
|
||||
end
|
||||
while i < j && nums[i] <= nums[left]
|
||||
i += 1 # Идти слева направо в поисках первого элемента больше опорного
|
||||
end
|
||||
# Обмен элементов
|
||||
nums[i], nums[j] = nums[j], nums[i]
|
||||
end
|
||||
# Переместить опорный элемент на границу двух подмассивов
|
||||
nums[i], nums[left] = nums[left], nums[i]
|
||||
i # Вернуть индекс опорного элемента
|
||||
end
|
||||
|
||||
# ## Быстрая сортировка (оптимизация глубины рекурсии) ###
|
||||
def quick_sort(nums, left, right)
|
||||
# Рекурсивно обрабатывать, пока длина подмассива не станет равной 1
|
||||
while left < right
|
||||
|
||||
@@ -4,9 +4,9 @@ comments: true
|
||||
|
||||
# 11.10 Поразрядная сортировка
|
||||
|
||||
В предыдущем разделе мы познакомились с сортировкой подсчетом: она подходит для случаев, когда объем данных $n$ велик, а диапазон значений $m$ сравнительно мал. Предположим теперь, что нужно отсортировать $n = 10^6$ студенческих идентификаторов, причем каждый идентификатор является $8$-значным числом. Тогда диапазон данных $m = 10^8$ оказывается очень большим; сортировка подсчетом потребует огромного объема памяти, а поразрядная сортировка позволяет этого избежать.
|
||||
В предыдущем разделе была рассмотрена сортировка подсчетом: она хорошо подходит для случаев, когда объем данных $n$ велик, а диапазон значений $m$ сравнительно мал. Предположим теперь, что нужно отсортировать $n = 10^6$ номеров студентов, причем каждый номер представляет собой $8$-значное число. Тогда диапазон данных $m = 10^8$ оказывается очень большим; сортировка подсчетом потребует огромного объема памяти, а поразрядная сортировка позволяет этого избежать.
|
||||
|
||||
<u>Поразрядная сортировка (radix sort)</u> по своей основной идее совпадает с сортировкой подсчетом и тоже реализует сортировку через подсчет количества. Поверх этого поразрядная сортировка использует иерархию разрядов числа и последовательно сортирует данные по каждому разряду, получая итоговый упорядоченный результат.
|
||||
<u>Поразрядная сортировка (radix sort)</u> по своей основной идее совпадает с сортировкой подсчетом и тоже реализует сортировку через подсчет количества. При этом поразрядная сортировка использует соотношение между разрядами числа и последовательно сортирует данные по каждому разряду, получая итоговый упорядоченный результат.
|
||||
|
||||
## 11.10.1 Алгоритм
|
||||
|
||||
@@ -26,7 +26,7 @@ $$
|
||||
x_k = \lfloor\frac{x}{d^{k-1}}\rfloor \bmod d
|
||||
$$
|
||||
|
||||
где $\lfloor a \rfloor$ обозначает округление числа $a$ вниз, а $\bmod \: d$ означает взятие остатка по модулю $d$ . Для студенческих идентификаторов выполняется $d = 10$ и $k \in [1, 8]$ .
|
||||
где $\lfloor a \rfloor$ обозначает округление числа $a$ вниз, а $\bmod \: d$ означает взятие остатка по модулю $d$ . Для студенческих номеров выполняется $d = 10$ и $k \in [1, 8]$ .
|
||||
|
||||
Кроме того, нам нужно слегка изменить код сортировки подсчетом, чтобы он мог сортировать числа по их $k$-му разряду:
|
||||
|
||||
@@ -675,7 +675,13 @@ $$
|
||||
(num / exp) % 10
|
||||
end
|
||||
|
||||
### Сортировка подсчетом (сортировка по k-му разряду nums) ###
|
||||
### Получить k-й разряд элемента num, где exp = 10^(k-1) ###
|
||||
def digit(num, exp)
|
||||
# Передача exp вместо k позволяет избежать повторного выполнения дорогостоящих вычислений степени
|
||||
(num / exp) % 10
|
||||
end
|
||||
|
||||
# ## Сортировка подсчетом (сортировка по k-му разряду nums) ###
|
||||
def counting_sort_digit(nums, exp)
|
||||
# Разряды десятичной системы лежат в диапазоне 0~9, поэтому нужен массив корзин длины 10
|
||||
counter = Array.new(10, 0)
|
||||
|
||||
@@ -9,8 +9,8 @@ comments: true
|
||||
Пусть длина массива равна $n$ ; тогда процесс сортировки выбором выглядит так, как показано на рисунке 11-2.
|
||||
|
||||
1. В начальном состоянии все элементы не отсортированы, то есть неотсортированный диапазон индексов равен $[0, n-1]$ .
|
||||
2. Выбрать минимальный элемент из диапазона $[0, n-1]$ и поменять его местами с элементом в позиции $0$ . После этого первые 1 элементов массива отсортированы.
|
||||
3. Выбрать минимальный элемент из диапазона $[1, n-1]$ и поменять его местами с элементом в позиции $1$ . После этого первые 2 элементов массива отсортированы.
|
||||
2. Выбрать минимальный элемент из диапазона $[0, n-1]$ и поменять его местами с элементом в позиции $0$ . После этого первый элемент массива отсортирован.
|
||||
3. Выбрать минимальный элемент из диапазона $[1, n-1]$ и поменять его местами с элементом в позиции $1$ . После этого первые два элемента массива отсортированы.
|
||||
4. Продолжать по аналогии. После $n - 1$ раундов выбора и обмена первые $n - 1$ элементов массива будут отсортированы.
|
||||
5. Оставшийся элемент обязательно является максимальным, сортировать его не нужно, поэтому массив считается отсортированным.
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ comments: true
|
||||
|
||||
# 11.1 Алгоритмы сортировки
|
||||
|
||||
<u>Алгоритмы сортировки (sorting algorithm)</u> используются для упорядочивания набора данных по определенному правилу. Они применяются очень широко, потому что упорядоченные данные обычно проще и быстрее искать, анализировать и обрабатывать.
|
||||
<u>Алгоритмы сортировки (sorting algorithm)</u> используются для упорядочивания набора данных по определенному правилу. Они применяются очень широко, потому что упорядоченные данные обычно проще анализировать, обрабатывать и искать в них нужные элементы.
|
||||
|
||||
Как показано на рисунке 11-1, данными в алгоритмах сортировки могут быть целые числа, числа с плавающей запятой, символы, строки и другие типы. Критерий сравнения тоже можно задать по-разному, например по величине чисел, по порядку ASCII-кодов символов или по пользовательскому правилу.
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ comments: true
|
||||
- Блочная сортировка включает три этапа: распределение данных по блокам, сортировку внутри блоков и объединение результатов. Она тоже отражает стратегию "разделяй и властвуй" и подходит для очень больших объемов данных. Ключ к эффективности блочной сортировки - равномерное распределение данных.
|
||||
- Сортировка подсчетом является частным случаем блочной сортировки; она реализует сортировку через подсчет числа вхождений данных. Сортировка подсчетом подходит для случаев, когда объем данных велик, но диапазон значений ограничен, и при этом данные можно преобразовать в положительные целые числа.
|
||||
- Поразрядная сортировка выполняет сортировку данных путем последовательной сортировки по каждому разряду и требует, чтобы данные можно было представить в виде чисел фиксированной разрядности.
|
||||
- В общем случае нам хотелось бы найти алгоритм сортировки, который одновременно обладал бы высокой эффективностью, стабильностью, свойством выполнения на месте и адаптивностью. Но, как и в других разделах алгоритмов и структур данных, не существует одного алгоритма сортировки, способного удовлетворить всем этим требованиям одновременно. На практике приходится выбирать подходящий алгоритм в зависимости от свойств данных.
|
||||
- В общем случае нам хотелось бы найти алгоритм сортировки, который одновременно обладал бы высокой эффективностью, стабильностью, выполнением на месте и адаптивностью. Но, как и в других разделах алгоритмов и структур данных, не существует одного алгоритма сортировки, способного удовлетворить всем этим требованиям одновременно. На практике приходится выбирать подходящий алгоритм в зависимости от свойств данных.
|
||||
- На рисунке 11-19 сравниваются эффективность, стабильность, выполнение на месте и адаптивность основных алгоритмов сортировки.
|
||||
|
||||
{ class="animation-figure" }
|
||||
|
||||
Reference in New Issue
Block a user