Вступление
Сортировка пузыря , иногда также упоминается как Опускание сортировки является одним из наиболее широко известных алгоритмов сортировки. Обычно один из первых сортировков, которые студенты CS встречаются из-за его простоты и то, что оно вполне интуитивно понятно и легко перевести в код.
Однако этот простой алгоритм показал плохую работу в реальных проблемах. Особенно по сравнению с более быстрыми, более популярными и широко используемыми алгоритмами, такими как Quicksort или слияние. Вот почему Bubble Sort используется в основном как учебный инструмент.
В этой статье мы объясним, как Bubble Sort работает и реализует его в JavaScript. Мы также проверим свое время сложность и сравниваем его с некоторыми другими алгоритмами сортировки.
Кроме того, мы реализуем один из его вариантов – Cocktail Shiker Сортировать в попытке оптимизировать его.
Сортировка пузыря
Bubble Sort – это алгоритм сортировки типа сравнения. Это означает, что это сравнивает Отдельные элементы в коллекции во время выполнения. В зависимости от типа данных и назначения, сравнение может быть сделано через реляционный оператор или через функцию пользовательской сравнения.
Идея сорта Bubble довольно проста. Начиная с начала коллекции, мы хотим сортировать – сравнивать элементы в паре. Если пара находится в желаемом порядке, мы ничего не делаем. Если это не так, мы поменяем элементы, оно состоит из.
Это сделано снова, и снова, пока все элементы в коллекции не сортируются. Давайте посмотрим на визуальное представление о том, как работает Bubble Sort:
Посмотрите на элемент со значением 8
Мы можем видеть это «пузыриться» с самого начала массива на свое место. Это то, откуда приходит имя «пузырька».
Реализация сортировки пузыря
Теперь, когда мы прошли над идеей Bubble Sort, мы можем начать с реализации:
function bubbleSort(inputArr) { let n = inputArr.length; for(let i = 0; i < n; i++) { for(let j = 0; j < n; j++) { // Comparing and swapping the elements if(inputArr[j] > inputArr[j+1]){ let t = inputArr[j]; inputArr[j] = inputArr[j+1]; inputArr[j+1] = t; } } } return inputArr; }
Внедрение довольно интуитивно понятно. Мы повторяемся через массив N
раз с для
петля, где N
это длина массива. Для каждой итерации мы «пузыри» элемент в его правильное место. Это делается через другое для
Цикл, который сравнивает элемент к его соседниму, переключая их, если это нужно.
Наконец, мы возвращаем отсортированный массив. Давайте заполним массив и сортировать его:
let inputArr = [5,1,4,2,8]; bubbleSort(inputArr); console.log(inputArr);
Запуск этого кода даст:
(5) [1, 2, 4, 5, 8]
Давайте посмотрим, как это делается с конкретными значениями:
Первая итерация:
[ 5 , 1 , 4, 2, 8] -> [ 1 , 5 , 4, 2, 8] – мы обменим 5 и 1, так как 5> 1 [1, 5 , 4 , 2, 8] -> [1, 4 , 5 , 2, 8] – мы обменим 5 и 4, так как 5> 4 [1, 4, 5 , 2 , 8] -> [1, 4, 2 , 5 8] – Мы обменим 5 и 2, с 5> 2 [1, 4, 2, 5 , 8 ] -> [1, 4, 2, 5 , 8 ] – Нет изменения, так как 5 <8
Вторая итерация:
[ 1 , 4 , 2, 5, 8] -> [ 1 , 4 , 2, 5, 8] – без изменений, с 1 <4 [1, 4 , 2 , 5, 8] -> [1, 2 , 4 , 5, 8] – мы обменим 4 и 2, так как 4> 2 [1, 2, 4 , 5 , 8] -> [1, 2, 4 , 5 8] – Без изменений, так как 4 <5 [1, 2, 4, 5 , 8 ] -> [1, 2, 4, 5 , 8 ] – Нет изменения, так как 5 <8
Массив сортируется в рамках двух итераций, однако наш алгоритм продолжит работу N
раз, сравнивая все элементы снова и снова. Это потому, что мы сказали это итеративным inportarr.length
раз.
Сортировка пузыря неэффективна в самом себе – особенно с ошибочным недостатком. Есть две вещи, которые мы можем сделать, чтобы оптимизировать его, хотя.
Оптимизация
Первая оптимизация, которую мы можем реализовать, является – завершить алгоритм, если массив отсортирован – то есть никаких свопов. Это можно сделать через логический
флаг. Каждый раз, когда мы поменяем любые элементы, он установлен на правда
:
function bubbleSort(inputArr) { let n = inputArr.length; let sorted = false; while (!sorted) { sorted = true; for(let i = 0; i < n; i++){ if(inputArr[i] > inputArr[i+1]){ let t = inputArr[i]; inputArr[i] = inputArr[i+1]; inputArr[i+1] = t; sorted = false; } } } return inputArr; }
Как только мы закончим итерацию через массив, и никаких свопов не было сделано, в то время как
Цикл прекратит петли, и массив возвращается.
Давайте загрузим массив снова и сортировать его:
let inputArr = [5,1,4,2,8]; bubbleSort(inputArr); console.log(inputArr);
Этот код приводит к себе:
[1, 2, 4, 5, 8]
То, что стоит отметить, это то, что с первой итерацией закончится, самый большой элемент будет расположен в конце массива. Следующая итерация будет размещать второй по величине элемент перед крупнейшим, и так далее.
Это означает, что с каждой итерацией нам не нужно смотреть на последний элемент, поскольку мы знаем, что это в нужном месте. Таким образом, в k-th Итерация, нам действительно нужно посмотреть на N-K + 1 Итерации:
function bubbleSort(inputArr) { let n = inputArr.length; let sorted = false; let numOfIterations = 0; while(!sorted) { sorted = true; for(let i = 0; i < n-numOfIterations+1; i++){ if(inputArr[i] > inputArr[i+1]){ let t = inputArr[i]; inputArr[i] = inputArr[i+1]; inputArr[i+1] = t; sorted = false; numOfIterations++; } } } return inputArr; }
Давайте загрузим массив снова и сортировать его:
let inputArr = [5,1,4,2,8]; bubbleSort(inputArr); console.log(inputArr);
Этот код приводит к себе:
(5) [1, 2, 4, 5, 8]
Cocktail Shaker Сортировать против Bubble Sort
Еще одна оптимизация пузырьковой сортировки – это ее полученный вариант называется Cocktail Shiker Сортировать , также известный как Двунаправленный пузырь сортирует или просто Коктейль Сортировка Отказ
Этот алгоритм простирает пузырь, сортируя, работая в двух направлениях. Вместо того, чтобы идти от начала до конца, и повторять это, он идет от начала до конца, а затем от финиширования, чтобы начать снова, в одной полной итерации. Эффективно, что он выполняет двойную работу пузырькового сорта в одной полной итерации, хотя на практике он обычно не выполняет в два раза быстрее.
Это потому, что он имеет аналогичное сравнение-счет. Это сравнивает больше элементов на итерацию, чем обычный пузырь, и он удвоит свопы на итерацию. Причина, по которой это быстрее, потому что диапазон возможных свопов на итерацию становится меньше и меньше, придавая ему немного лучшего характеристики.
Давайте пойдем вперед и реализуем алгоритм:
function cocktailShakerSort(inputArr) { let n = inputArr.length; let sorted = false; while (!sorted) { sorted = true; for (let i = 0; i < n - 1; i++) { if (inputArr[i] > inputArr[i + 1]){ let tmp = inputArr[i]; inputArr[i] = inputArr[i + 1]; inputArr[i+1] = tmp; sorted = false; } } if (sorted) break; sorted = true; for (let j = n - 1; j > 0; j--) { if (inputArr[j-1] > inputArr[j]) { let tmp = inputArr[j]; inputArr[j] = inputArr[j + 1]; inputArr[j+1] = tmp; sorted = false; } } } return inputArr; }
Первая часть такая же, как обычный пузырь. Хотя после того, как мы пройдем вперед, мы идем назад. Во-первых, мы проверяем, сортируется ли массив с предыдущим предыдущим проходом. Если нет, мы возвращаемся назад, обменивайтесь при необходимости. Если нет свопом, алгоритм прекращается, и результат возвращается.
Если мы не проверили ловушки во втором проходе, нам придется пройти дополнительное время вперед для проверки, если массив отсортирован.
Давайте посмотрим на руководство по примеру от ранее – на этот раз, с коктейлем:
[ 5 , 1 , 4, 2, 8] -> [ 1 , 5 , 4, 2, 8] – мы обменим 5 и 1, так как 5> 1 [1, 5 , 4 , 2, 8] -> [1, 4 , 5 , 2, 8] – мы обменим 5 и 4, так как 5> 4 [1, 4, 5 , 2 , 8] -> [1, 4, 2 , 5 8] – Мы обменим 5 и 2, с 5> 2 [1, 4, 2, 5 , 8 ] -> [1, 4, 2, 5 , 8 ] – Нет изменения, так как 5 <8 [1, 4, 2 , 5 , 8] -> [1, 4, 2 , 5 , 8] – Нет изменения, с 5> 2 [1, 4 , 2 , 5, 8] -> [1, 2 , 4 , 5, 8] – мы обменим 4 и 2, так как 2 <4 [ 1 , 2 , 4, 5, 8] -> [ 1 , 2 , 4, 5, 8] – без изменений, с 2> 1
Здесь наш массив отсортирован в пределах 1 итерации, в отличие от 2 итераций Bubble Worth. Сортировка коктейля сделала это с 7 сравнениями, тогда как пузырь сорт сделали это с 8. Это не очень много в этой шкале, хотя с большим количеством, мы увидим повышение производительности.
Как правило, это приводит к на 33% быстрее производительности.
Дональд Е. Кнут упомянул коктейль-шейкер сортировки, наряду с несколькими подобными вариантами сорта пузыря, в его знаменитой монографии «Искусство компьютерного программирования» :
Но ни одно из этих усовершенствований не приводит к алгоритму лучше, чем прямая вставка [то есть, вставка]; И мы уже знаем, что прямая вставка не подходит для больших N. […]
Короче говоря, сорта пузырьков, похоже, не имеет ничего, чтобы порекомендовать его, кроме броского имени и того, что оно приводит к некоторым интересным теоретическим проблемам.
Сложность и сравнение времени
Так как наш массив содержит N
Элементы, Bubble Sort Выполняет O (n) Сравнения, N
раз. Это приводит нас к общему времени работы O (n 2 ) – средний и худший случай. Это ужасная сложность времени для алгоритма сортировки.
Для справки, наиболее распространенные алгоритмы сортировки, такие как QuickSort или Merge Sort, имеют среднее время работы O (nlogn) Отказ
Теоретически, пузырька может иметь O (n) Сложность, если мы запустим его в отсортированной коллекции, которая превосходит Все Другие алгоритмы, кроме вставки Sort и Cube Sort. Хотя редкость этого случая не оправдывает использование его на практике.
Использование встроенного Консоль. Время ()
Функция, мы можем сравнить время, необходимое для запуска кода на массивах разных длин:
console.time('bubble'); bubbleSort(inputArr); console.timeEnd('bubble');
Мы будем делать это для массивов размеров 100 , 1 000 и 10 000 :
100 | 1 мс. | 2 мс. | 1 мс. | 1 мс. |
1000 | 1 мс. | 8 мс. | 6 мс. | 1 мс. |
10 000 | 1 мс. | 402 мс. | 383 мс. | 2 мс. |
Здесь очевидно, насколько неэффективно первая реализация сравнивается с вариантами, такими как коктейль-шейкер.
Заключение
Хотя Bubble Sort является очень интуитивно понятным и простым для понимания и реализации, очень нецелесообразно для решения большинства проблем.
Он имеет среднее и худшее время работы O (n 2 ) и может работать только на его лучшее время работы O (n) Когда массив уже отсортирован.
Его космическая сложность – O (1) , что это Отличный Отказ К сожалению, это почти не достаточно, чтобы восполнить ужасную сложность.
Даже среди простых O (n 2 ) Алгоритмы сортировки, сортировка вставки или сортировки выбора обычно значительно более эффективны.
Благодаря своей простоте, Bubble Sort часто используется в качестве введения в сортировку алгоритмов на вводных курсах информатики.