Рубрики
Без рубрики

Bubble Sort и Cocktail Shiker Сортировать в JavaScript

В этом руководстве мы будем реализовывать и оптимизировать пузырьковый сортировку и коктейль-шейкер в JavaScript с примерами. Мы также выполним большой анализ O.

Автор оригинала: Mila Lukic.

Вступление

Сортировка пузыря , иногда также упоминается как Опускание сортировки является одним из наиболее широко известных алгоритмов сортировки. Обычно один из первых сортировков, которые студенты 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 часто используется в качестве введения в сортировку алгоритмов на вводных курсах информатики.