5.3. Демонстрация - Реализация сортировки пузырьком

Шаг 1

В этой демонстрации реализуем на практике сортировку пузырьком из предыдущей статьи. Будет лучше, если сначала вы попробуйте реализовать её самостоятельно!


/*
bubbleSort([5, 3, 2, 1]) => [1, 2, 3, 5]
bubbleSort([1, 2, 3]) => [1, 2, 3]
*/

function bubbleSort(array) {
  return [];
}

Шаг 2

Сначала напишем простую версию, без разделения на сортированный и несортированный подмассивы, а потом оптимизируем её. Для этого нам нужно проитерироваться по всему массиву n раз.


import {logIteration} from "./logger";

/*
bubbleSort([5, 3, 2, 1]) => [1, 2, 3, 5]
bubbleSort([1, 2, 3]) => [1, 2, 3]
*/

function bubbleSort(array) {
  // проходим весь массив столько раз, сколько в нём элементов - 1, потому что, как мы выяснили в статье,
  // последняя итерация "добьет" сразу два последних элемента
  for (let i = 0; i < array.length - 1; i++) {
    // в рамках каждого прохода нам тоже нужно просматривать на один элемент меньше,
    // ведь для последнего элемента мы не найдем следующий, с которым будем его сравнивать
    for (let j = 0; j < array.length - 1; j++) {
      // ...
    }

    logIteration(array);
  }

  return array;
}

bubbleSort([56, 87, 18, 92, 42, 31, 44, 82, 36, 91]);

Шаг 3

Осталось лишь дописать попарное сравнение элементов и их смену, и наша сортировка готова!


import {logIteration} from "./logger";

/*
bubbleSort([5, 3, 2, 1]) => [1, 2, 3, 5]
bubbleSort([1, 2, 3]) => [1, 2, 3]
*/

function bubbleSort(array) {
  // проходим весь массив столько раз, сколько в нём элементов - 1, потому что, как мы выяснили в статье,
  // последняя итерация "добьет" сразу два последних элемента
  for (let i = 0; i < array.length - 1; i++) {
    // в рамках каждого прохода нам тоже нужно просматривать на один элемент меньше,
    // ведь для последнего элемента мы не найдем следующий, с которым будем его сравнивать
    for (let j = 0; j < array.length - 1; j++) {
      // если элемент слева больше элемента справа...
      if (array[j] > array[j + 1]) {
        /// ... меняем их местами
        [array[j], array[j + 1]] = [array[j + 1], array[j]];
      }
    }

    logIteration(array);
  }

  return array;
}

bubbleSort([56, 87, 18, 92, 42, 31, 44, 82, 36, 91]);

Шаг 4

А теперь можем дописать небольшую оптимизацию. Мы точно знаем, что в каждый проход самое большое число «всплывает» в конец массива, поэтому и проходить по ним каждый раз не нужно. Каждый проход будем игнорировать уже отсортированную часть массива. Важно понимать, что хотя мы и отсекли часть ненужных проходов, общая сложность алгоритма всё ещё квадратичная! Можете убедиться в этом сами, например, посчитав количество операций для списка из трёх и четырёх элементов.

import {logIteration} from "./logger";

/*
bubbleSort([5, 3, 2, 1]) => [1, 2, 3, 5]
bubbleSort([1, 2, 3]) => [1, 2, 3]
*/

function bubbleSort(array) {
  // проходим весь массив столько раз, сколько в нём элементов - 1, потому что, как мы выяснили в статье,
  // последняя итерация "добьет" сразу два последних элемента
  for (let i = 0; i < array.length - 1; i++) {
    // в рамках каждого прохода нам тоже нужно просматривать на один элемент меньше,
    // ведь для последнего элемента мы не найдем следующий, с которым будем его сравнивать
    // а ещё каждую итерацию будем смотреть на один элемент поменьше, ведь справа будет уже сортированная его часть
    for (let j = 0; j < array.length - 1 - i; j++) {
      // если элемент слева больше элемента справа...
      if (array[j] > array[j + 1]) {
        /// ... меняем их местами
        [array[j], array[j + 1]] = [array[j + 1], array[j]];
      }
    }

    logIteration(array);
  }

  return array;
}

bubbleSort([56, 87, 18, 92, 42, 31, 44, 82, 36, 91]);

File logger

const results = document.getElementById('results')

export function logIteration(array) {
  const result = document.createElement('li');

  result.innerHTML = `[${array.join(', ')}]`;

  results.appendChild(result);
}