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

Алгоритмы сортировки объяснены примерами в Python, Java и C ++

Автор оригинала: FreeCodeCapm Team.

Что такое алгоритм сортировки?

Алгоритмы сортировки – это набор инструкций, которые принимают массив или список в качестве ввода и устраивают элементы в определенный порядок.

Сортировки чаще всего чаще всего в численности или форме алфавитного (называемого лексикографического) порядка, и могут быть в порядке возрастания (A-Z, 0-9) или нисходящего (Z-A, 9-0).

Почему алгоритмы сортировки важны

Поскольку сортировка часто может уменьшить сложность проблемы, это важный алгоритм информатики. Эти алгоритмы имеют прямые приложения в алгоритмах поиска, алгоритмам баз данных, методах разделения и завоеваний, алгоритмам структуры данных и многих других.

Компромиссы алгоритмов

При использовании разных алгоритмов необходимо спросить несколько вопросов. Насколько большая коллекция сортируется? Сколько памяти в распоряжении для использования? Коллекция должна расти?

Ответы на эти вопросы могут определить, какой алгоритм будет работать лучше всего для ситуации. Некоторые алгоритмы, такие как сорта слияния, могут потребоваться много места для запуска, а вставка не всегда самая быстрая, но не требует многих ресурсов для запуска.

Вы должны определить, какие требования системы и его ограничения до принятия решения о том, какой алгоритм использовать.

Некоторые распространенные алгоритмы сортировки

Некоторые из наиболее распространенных алгоритмов сортировки являются:

  • Сортировка отбора
  • Сортировка пузыря
  • Сортировка вставки
  • Сортировка слиянием
  • Быстрый сортировка
  • Сортировка кучи
  • Подсчет сортировки
  • Radix Sort.
  • Сортировка ведра

Но прежде чем мы попадаем в каждый из них, давайте узнаем немного больше о том, что делает классифицирует алгоритм сортировки.

Классификация алгоритма сортировки

Алгоритмы сортировки могут быть классифицированы на основе следующих параметров:

  1. На основании количества свопов или инверсии Это количество раз, когда алгоритм разваливается элементы для сортировки входа. Сортировка отбора требует минимального количества свопов.
  2. Основываясь на количестве сравнений, это количество раз, когда алгоритм сравнивает элементы для сортировки ввода. Использование Обозначение Big-O Приведенные выше приведенные выше примеры алгоритма сортировки требуют, по крайней мере, O (nlogn) Сравнение в лучшем случае и O (n ^ 2) Сравнение в худшем случае для большинства выходов.
  3. На основании рекурсии или нерешительности некоторые алгоритмы сортировки, такие как Быстрая сортировка Используйте рекурсивные методы для сортировки ввода. Другие алгоритмы сортировки, такие как Сортировка отбора или Сортировка вставки Используйте нерекурсивные методы. Наконец, какой-то алгоритм сортировки, например Сортировка слияния Используйте как рекурсивные, а также нерешительные методы для сортировки ввода.
  4. На основании алгоритмов сортировки стабильности называются Стабильный Если алгоритм поддерживает относительный порядок элементов с равными клавишами. Другими словами, два эквивалентных элемента остаются в том же порядке в отсортированном выходе, поскольку они были на входе.
  5. Сортировка вставки , Сортировка слияния и Сортировка пузыря стабильны
  6. Куча сортировки и Быстрая сортировка не стабильны
  7. На основании дополнительных алгоритмов сортировки требований к пространству называются на месте Если им требуется постоянная O (1) Дополнительное пространство для сортировки.
  8. Сортировка вставки и Quick-sort являются на месте Сортировка, поскольку мы перемещаем элементы о повороте и на самом деле не используем отдельный массив, который не является случай в соревнованиях слияния, где размер ввода должен быть распределен заранее, чтобы сохранить вывод во время своего рода.
  9. Сортировка слияния является примером out place Сортировать, так как требуется дополнительное пространство памяти для его операций.

Лучшая возможная сложность времени для любой сортировки на основе сравнения

Любое алгоритм сортировки на основе сравнения должен сделать, по крайней мере, сравнения NLOG2N для сортировки входного массива, а Heapsort и сортировки слияния являются асимптотически оптимальными сортами сравнения. Это может быть легко доказано путем рисования диаграммы дерева решений.

Сортировка ведра

Сортировка ковша – это алгоритм сортировки сравнения, который работает на элементах, разделив их в разные ведра, а затем сортируя эти ведра индивидуально. Каждое ведро сортируется индивидуально, используя отдельный алгоритм сортировки или применяя алгоритм сортировки ведра рекурсивно.

Сортировка ведра в основном полезен при равномерно распределении ввода по диапазону.

Предположим, у вас есть следующая проблема перед вами

Вам дали большой массив целых чисел плавающих точек, лежащих равномерно между нижней и верхней границей. Этот массив теперь должен быть отсортирован.

Простой способ решить эту проблему, будет использовать другой алгоритм сортировки, такой как сортировка слияния, сортировка кучи или быстрым сортировке. Тем не менее, эти алгоритмы гарантируют наилучшую сложность случая O (NLOGN). Однако, используя сортировку ведра, приведенная выше задача может быть завершена в O (N) времени.

Давайте посмотрим на это.

Подумайте, что вам нужно создать массив списков, то есть ведра. Теперь элементы должны быть вставлены в эти ведра на основе их свойств. Каждая из этих ведер может быть отсортирована индивидуально с использованием вставки.

Псевдо код для ведра Сортировка:

void bucketSort(float[] a,int n)

{

    for(each floating integer 'x' in n)

    {
        insert x into bucket[n*x]; 
    }

    for(each bucket)

    {
        sort(bucket);
    }

}

Подсчет сортировки

Сортировка подсчета – это методика сортировки на основе ключей между определенным диапазоном. Он работает, считая количество объектов, имеющих различные ключевые значения (вида хеширования). Затем выполняя арифметику для расчета положения каждого объекта в выходной последовательности.

Пример:

For simplicity, consider the data in the range 0 to 9. 
Input data: 1, 4, 1, 2, 7, 5, 2
  1) Take a count array to store the count of each unique object.
  Index:     0  1  2  3  4  5  6  7  8  9
  Count:     0  2  2  0  1  1  0  1  0  0

  2) Modify the count array such that each element at each index 
  stores the sum of previous counts. 
  Index:     0  1  2  3  4  5  6  7  8  9
  Count:     0  2  4  4  5  6  6  7  7  7

The modified count array indicates the position of each object in 
the output sequence.
 
  3) Output each object from the input sequence followed by 
  decreasing its count by 1.
  Process the input data: 1, 4, 1, 2, 7, 5, 2. Position of 1 is 2.
  Put data 1 at index 2 in output. Decrease count by 1 to place 
  next data 1 at an index 1 smaller than this index.
  

Характеристики

  • Космическая сложность: O (k)
  • Лучший случай производительности: o (n + k)
  • Средняя производительность дела: o (n + k)
  • Худшее представление: O (n + k)
  • Стабильный: Да (k – это количество различных элементов в массиве)

Реализация в JavaScript

let numbers = [1, 4, 1, 2, 7, 5, 2];
let count = [];
let i, z = 0;
let max = Math.max(...numbers);      
// initialize counter
for (i = 0; i <= max; i++) {
    count[i] = 0;
}
for (i=0; i < numbers.length; i++) {
    count[numbers[i]]++;
}
for (i = 0; i <= max; i++) {
    while (count[i]-- > 0) {
        numbers[z++] = i;
    }
}
// output sorted array
for (i=0; i < numbers.length; i++) {
    console.log(numbers[i]);
}

Реализация C ++

#include 

void countSort(int upperBound, int lowerBound, std::vector numbersToSort) //lower and upper bounds of numbers in vector
{
  int range = upperBound - lowerBound;                  //create a range large enough to get every number between the min and max
  std::vector counts (range);                      //initialize of counts of the size of the range
  std::fill(counts.begin(), counts.end(), 0);           //fill vector of zeros
  
  for (int i = 0; i < numbersToSort.size(); i++)
  {
      int index = numbersToSort[i] - lowerBound; //For example, if 5 is the lower bound and numbersToSort[i] is 5. index will be 0 and the       counts[index]+= 1;                         //count of 5 will be stored in counts[0]
  }
  
  std::cout << counts << std::endl;
} 

Свифт реализации

func countingSort(_ array: [Int]) {
  // Create an array to store the count of each element
  let maxElement = array.max() ?? 0
  var countArray = [Int](repeating: 0, count: Int(maxElement + 1))
  
  for element in array {
    countArray[element] += 1
  }
  var z = 0
  var sortedArray = [Int](repeating: 0, count: array.count)

  for index in 1 ..< countArray.count {
    //print index element required number of times
    while countArray[index] > 0 {
      sortedArray[z] = index
      z += 1
      countArray[index] -= 1
    }
  }
  
  print(sortedArray)
}

Сортировка вставки

Сортировка вставки – это простой алгоритм сортировки для небольшого количества элементов.

Пример:

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

Начните с индекса 1 до размера входного массива.

[8 3 5 1 4 2]

Шаг 1:

      key = 3 //starting from 1st index.

      Here `key` will be compared with the previous elements.

      In this case, `key` is compared with 8. since 8 > 3, move the element 8
      to the next position and insert `key` to the previous position.

      Result: [ 3 8 5 1 4 2 ]

Шаг 2:

      key = 5 //2nd index

      8 > 5 //move 8 to 2nd index and insert 5 to the 1st index.

      Result: [ 3 5 8 1 4 2 ]

Шаг 3:

      key = 1 //3rd index

      8 > 1     => [ 3 5 1 8 4 2 ]  

      5 > 1     => [ 3 1 5 8 4 2 ]

      3 > 1     => [ 1 3 5 8 4 2 ]

      Result: [ 1 3 5 8 4 2 ]

Шаг 4:

      key = 4 //4th index

      8 > 4   => [ 1 3 5 4 8 2 ]

      5 > 4   => [ 1 3 4 5 8 2 ]

      3 > 4   ≠>  stop

      Result: [ 1 3 4 5 8 2 ]

Шаг 5:

      key = 2 //5th index

      8 > 2   => [ 1 3 4 5 2 8 ]

      5 > 2   => [ 1 3 4 2 5 8 ]

      4 > 2   => [ 1 3 2 4 5 8 ]

      3 > 2   => [ 1 2 3 4 5 8 ]

      1 > 2   ≠> stop

      Result: [1 2 3 4 5 8]

Алгоритм, показанный ниже, – слегка оптимизированная версия, чтобы избежать замены ключ элемент в каждой итерации. Здесь ключ Элемент будет поменяться в конце итерации (шаг).

    InsertionSort(arr[])
      for j = 1 to arr.length
         key = arr[j]
         i = j - 1
         while i > 0 and arr[i] > key
            arr[i+1] = arr[i]
            i = i - 1
         arr[i+1] = key

Вот подробная реализация в JavaScript:

function insertion_sort(A) {
    var len = array_length(A);
    var i = 1;
    while (i < len) {
        var x = A[i];
        var j = i - 1;
        while (j >= 0 && A[j] > x) {
            A[j + 1] = A[j];
            j = j - 1;
        }
        A[j+1] = x;
        i = i + 1;
    }
}

Быстрая реализация в SWIFT показана ниже:

  var array = [8, 3, 5, 1, 4, 2]

  func insertionSort(array:inout Array) -> Array{
      for j in 0.. 0 && array[i] > key){
              array[i+1] = array[i]
              i = i-1
          }
          array[i+1] = key
      }
      return array
  }

Пример Java показан ниже:

public int[] insertionSort(int[] arr)
      for (j = 1; j < arr.length; j++) {
         int key = arr[j]
         int i = j - 1
         while (i > 0 and arr[i] > key) {
            arr[i+1] = arr[i]
            i -= 1
         }
         arr[i+1] = key
      }
      return arr;

А в с ….

void insertionSort(int arr[], int n) 
{ 
   int i, key, j; 
   for (i = 1; i < n; i++) 
   { 
       key = arr[i]; 
       j = i-1;
       while (j >= 0 && arr[j] > key) 
       { 
           arr[j+1] = arr[j]; 
           j = j-1; 
       } 
       arr[j+1] = key; 
   } 
} 

Характеристики:

  • Космическая сложность: O (1)
  • Сложность времени: o (n), o (n * n), o (n * n) для лучшего, среднего, наихудших случаев соответственно.
  • Лучший случай: массив уже отсортирован
  • Средний случай: массив случайным образом отсортирован
  • Худший случай: массив обратно отсортирован.
  • Сортировка на месте: да
  • Стабильно: Да

Heapsort.

Heapsort – это эффективный алгоритм сортировки, основанный на использовании макс/мин. Куча – это структура данных на основе деревьев, которая удовлетворяет свойство кучи – то есть для максимальной кучи, ключ любого узла меньше или равен ключению его родителя (если у него есть родитель).

Это свойство может быть использовано для доступа к максимальному элементу в количестве кучи в O (logn), используя метод Maxhapfify. Мы выполняем эту операцию N раз, каждый раз перемещая максимальный элемент в куче в верхнюю часть кучи и извлечь его из кучи и в отсортированную массив. Таким образом, после N итераций у нас будет отсортированная версия входного массива.

Алгоритм не является алгоритмом на месте и потребует в первую очередь структуру данных кучи. Алгоритм также является нестабильным, что означает при сравнении объектов с той же ключевым, исходное упорядочение не будет сохранено.

Этот алгоритм запускается в O (NLONGE) и O (1) дополнительным пространством [o (n), включая пространство, необходимое для хранения входных данных], поскольку все операции выполняются полностью на месте.

Лучшая, худшая и средняя сложность применения Heapsort o (nlogn). Хотя Heapsort имеет лучшее ухудшение сложности, чем QuickSort, хорошо реализованный QuickSort проходит быстрее на практике. Это алгоритм на основе сравнения, поэтому его можно использовать для нечисленных наборов данных, поскольку некоторые соотношения (свойство кучи) могут быть определены над элементами.

Реализация в Java, как показано ниже:

import java.util.Arrays;
public class Heapsort {

	public static void main(String[] args) {
		//test array
		Integer[] arr = {1, 4, 3, 2, 64, 3, 2, 4, 5, 5, 2, 12, 14, 5, 3, 0, -1};
		String[] strarr = {"hope you find this helpful!", "wef", "rg", "q2rq2r", "avs", "erhijer0g", "ewofij", "gwe", "q", "random"};
		arr = heapsort(arr);
		strarr = heapsort(strarr);
		System.out.println(Arrays.toString(arr));
		System.out.println(Arrays.toString(strarr));
	}
	
	//O(nlogn) TIME, O(1) SPACE, NOT STABLE
	public static > E[] heapsort(E[] arr){
		int heaplength = arr.length;
		for(int i = arr.length/2; i>0;i--){
			arr = maxheapify(arr, i, heaplength);
		}
		
		for(int i=arr.length-1;i>=0;i--){
			E max = arr[0];
			arr[0] = arr[i];
			arr[i] = max;
			heaplength--;
			arr = maxheapify(arr, 1, heaplength);
		}
		
		return arr;
	}
	
	//Creates maxheap from array
	public static > E[] maxheapify(E[] arr, Integer node, Integer heaplength){
		Integer left = node*2;
		Integer right = node*2+1;
		Integer largest = node;
		
		if(left.compareTo(heaplength) <=0 && arr[left-1].compareTo(arr[node-1]) >= 0){
			largest = left;
		}
		if(right.compareTo(heaplength) <= 0 && arr[right-1].compareTo(arr[largest-1]) >= 0){
			largest = right;
		}	
		if(largest != node){
			E temp = arr[node-1];
			arr[node-1] = arr[largest-1];
			arr[largest-1] = temp;
			maxheapify(arr, largest, heaplength);
		}
		return arr;
	}
}

Реализация в C ++

#include 
using namespace std;
void heapify(int arr[], int n, int i) 
{ 
    int largest = i; 
    int l = 2*i + 1;  
    int r = 2*i + 2;
    if (l < n && arr[l] > arr[largest]) 
        largest = l;
    if (r < n && arr[r] > arr[largest]) 
        largest = r;
    if (largest != i) 
    { 
        swap(arr[i], arr[largest]); 
  
        
        heapify(arr, n, largest); 
    } 
} 
  
 
void heapSort(int arr[], int n) 
{ 
    
    for (int i = n / 2 - 1; i >= 0; i--) 
        heapify(arr, n, i); 
  
    
    for (int i=n-1; i>=0; i--) 
    { 

        swap(arr[0], arr[i]); 
  
        
        heapify(arr, i, 0); 
    } 
} 
void printArray(int arr[], int n) 
{ 
    for (int i=0; i

Radix Sort.

Пререквизит: подсчет рода

QuickSort, Mergeort и Heapsort являются алгоритмами сортировки на основе сравнения. Countsort нет. Он имеет сложность O (N + K), где K – максимальный элемент входного массива. Таким образом, если k ì o (n), countsort становится линейной сортировкой, что лучше, чем сортировочные алгоритмы сортировки на основе сравнения, которые имеют o (nlogn) время сложности.

Идея состоит в том, чтобы продлить алгоритм CounterSort, чтобы получить лучшую сложность времени, когда K проходит O (N2). Вот идея радикса сортировки.

Алгоритм:

Для каждой цифры I, где я варьируюсь от наименее значимой цифры до самой значительной цифры числа, сортировка входного массива с использованием алгоритма CountSort в соответствии с i-й цифрой. Мы использовали счетчик сортировки, потому что это стабильный сорт.

Пример: предположим, что входной массив:

10, 21, 17, 34, 44, 11, 654, 123

На основании алгоритма мы сортируем входной массив в соответствии с цифрой (наименее значимой цифрой).

0: 10 1: 21 11 2: 3: 123 4: 34 44 654 5: 6: 7: 17 8: 9:

Итак, массив становится 10, 21, 11, 123, 24, 44, 654, 17.

Теперь мы сортируемся в соответствии с десятью цифрой:

0: 1: 10 11 17 2: 21 123 3: 34 4: 44 5: 654 6: 7: 8: 9:

Теперь массив становится: 10, 11, 17, 21, 123, 34, 44, 654.

Наконец, мы сортируем согласно цифре сотен (наиболее значимой цифры):

0: 010 011 017 021 034 044 1: 123 2: 3: 4: 5: 6: 654 7: 8: 9: 9:

Массив становится: 10, 11, 17, 21, 34, 44, 123, 654, который отсортирован. Вот как работает наш алгоритм.

Реализация в C:

void countsort(int arr[],int n,int place){

        int i,freq[range]={0};         //range for integers is 10 as digits range from 0-9
        int output[n];

        for(i=0;i=0;i--){
                output[freq[(arr[i]/place)%range]-1]=arr[i];
                freq[(arr[i]/place)%range]--;
        }
        
        for(i=0;i

Сортировка отбора

Сортировка отбора является одним из простейших алгоритмов сортировки. Этот алгоритм получает свое имя от того, как он итерации через массив: он выбирает текущий наименьший элемент, и вмешивает его на место.

Вот как это работает:

  1. Найдите наименьший элемент в массиве и поменяйте его первым элементом.
  2. Найдите второй маленький элемент и поменяйте со с помощью второго элемента в массиве.
  3. Найдите третий маленький элемент и поменяйте с помощью третьего элемента в массиве.
  4. Повторите процесс нахождения следующего наименьшего элемента и поднимая его в правильное положение, пока весь массив не будет отсортирован.

Но как бы вы написали код для поиска индекса второго наименьшего значения в массиве?

Легкий способ – заметить, что наименьшее значение уже поменялось в индекс 0, поэтому проблема уменьшает нахождение наименьшего элемента в массиве, начиная с индекса 1.

Сортировка отбора всегда занимает одинаковое количество ключевых сравнений – n (n – 1)/2.

Реализация в C/C ++

Следующая программа C ++ содержит итерацию, а также рекурсивную реализацию алгоритма сортировки отбора. Оба реализации вызываются в Главная () функция.

#include 
#include 
using namespace std;

template
void print_array(T const(&arr)[n]) {
    for (size_t i = 0; i < n; i++)
        std::cout << arr[i] << ' ';
    cout << "\n";
}

int minIndex(int a[], int i, int j) {
    if (i == j)
        return i;
    int k = minIndex(a, i + 1, j);
    return (a[i] < a[k]) ? i : k;
}

void recurSelectionSort(int a[], int n, int index = 0) {
    if (index == n)
        return;
    int k = minIndex(a, index, n - 1);
    if (k != index)
        swap(a[k], a[index]);
    recurSelectionSort(a, n, index + 1);
}

void iterSelectionSort(int a[], int n) {
    for (int i = 0; i < n; i++)
    {
        int min_index = i;
        int min_element = a[i];
        for (int j = i + 1; j < n; j++)
        {
            if (a[j] < min_element)
            {
                min_element = a[j];
                min_index = j;
            }
        }
        swap(a[i], a[min_index]);
    }
}

int main() {
    int recurArr[6] = { 100,35, 500, 9, 67, 20 };
    int iterArr[5] = { 25, 0, 500, 56, 98 };

    cout << "Recursive Selection Sort"  << "\n";
    print_array(recurArr); // 100 35 500 9 67 20
    recurSelectionSort(recurArr, 6);
    print_array(recurArr); // 9 20 35 67 100 500

    cout << "Iterative Selection Sort" << "\n";
    print_array(iterArr); // 25 0 500 56 98
    iterSelectionSort(iterArr, 5);
    print_array(iterArr); // 0 25 56 98 500
}

Реализация в JavaScript

function selection_sort(A) {
    var len = A.length;
    for (var i = 0; i < len - 1; i = i + 1) {
        var j_min = i;
        for (var j = i + 1; j < len; j = j + 1) {
            if (A[j] < A[j_min]) {
                j_min = j;
            } else {}
        }
        if (j_min !== i) {
            swap(A, i, j_min);
        } else {}
    }
}

function swap(A, x, y) {
    var temp = A[x];
    A[x] = A[y];
    A[y] = temp;
}

Реализация в Python

def seletion_sort(arr):
         if not arr:
         return arr
    for i in range(len(arr)):
         min_i = i
         for j in range(i + 1, len(arr)):
              if arr[j] < arr[min_i]:
                  min_i = j
         arr[i], arr[min_i] = arr[min_i], arr[i]

Реализация в Java

public void selectionsort(int array[])
{
    int n = array.length;            //method to find length of array 
    for (int i = 0; i < n-1; i++)
    {
        int index = i;
        int min = array[i];          // taking the min element as ith element of array
        for (int j = i+1; j < n; j++)
        {
            if (array[j] < array[index])
            {
                index = j;
                min = array[j];
            }
        }
        int t = array[index];         //Interchange the places of the elements
        array[index] = array[i];
        array[i] = t;
    }
}

Реализация в Матлаб

function [sorted] = selectionSort(unsorted)
    len = length(unsorted);
    for i = 1:1:len
        minInd = i;
        for j = i+1:1:len
           if unsorted(j) < unsorted(minInd) 
               minInd = j;
           end
        end
        unsorted([i minInd]) = unsorted([minInd i]);    
    end
    sorted = unsorted;
end

Характеристики

  • Космическая сложность: На)
  • Сложность времени: O (n2)
  • Сортировка на месте: да
  • Стабильный: Нет

Сортировка пузыря

Так же, как путь пузыри поднимаются со дна стекла, Сортировка пузыря Это простой алгоритм, который сортирует список, позволяющий либо более низким или более высоким значениям, чтобы позвонить до верха. Алгоритм проходит список и сравнивает соседние значения, заменяйте их, если они не в правильном порядке.

С наихудшими случаями сложности O (N ^ 2), пузырьковый сорт очень медленный по сравнению с другими алгоритмами сортировки, такими как QuickSort. Увеличивается, что это один из самых простых алгоритмов сортировки для понимания и кода с нуля.

С технической точки зрения, пузырьковый сорт является разумным для сортировки малых массивов или специально при выполнении алгоритмов сортировки на компьютерах с замечательно ограниченными ресурсами памяти.

Пример:

Сначала пройдите через список:

  • Начиная с [4, 2, 6, 3, 9] Алгоритм сравнивает первые два элемента в массиве, 4 и 2. Это сводит их, потому что 2 <4: [2, 4, 6, 3, 9]
  • Он сравнивает следующие два значения, 4 и 6. Как 4 <6, это уже в порядке, а алгоритм перемещается на: [2, 4, 6, 3, 9]
  • Следующие два значения также поменяются, потому что 3 <6: [2, 4, 3, 6, 9]
  • Последние два значения, 6 и 9, уже находятся в порядке, поэтому алгоритм не обменятся на них.

Второй проход через список:

  • 2 <4, так что нет необходимости подсказывать позиции: [2, 4, 3, 6, 9]
  • Алгоритм сворачивает следующие два значения, потому что 3 <4: [2, 3, 4, 6, 9]
  • Нет свопа как 4 <6: [2, 3, 4, 6, 9]
  • Опять же, 6 <9, так что нет свопа: [2, 3, 4, 6, 9]

Список уже отсортирован, но алгоритм сортировки пузыря не осознает это. Скорее, ему необходимо заполнить весь проход через список, не заменяя какие-либо значения, чтобы узнать список сортировки.

Третий проход через список:

  • [2, 4, 3, 6, 9] => [2, 4, 3, 6, 9]
  • [2, 4, 3, 6, 9] => [2, 4, 3, 6, 9]
  • [2, 4, 3, 6, 9] => [2, 4, 3, 6, 9]
  • [2, 4, 3, 6, 9] => [2, 4, 3, 6, 9]

Очевидно, что пузырь – это далеко неэффективный сортировочный алгоритм. Тем не менее, просто обернуть голову и реализовать себя.

Характеристики

  • Космическая сложность: O (1)
  • Лучший случай производительности: o (n)
  • Средняя производительность дела: o (n * n)
  • Худшая производительность: o (n * n)
  • Стабильно: Да

Видео объяснение

Алгоритм сортировки пузыря

Пример в JavaScript

let arr = [1, 4, 7, 45, 7,43, 44, 25, 6, 4, 6, 9],
    sorted = false;

while(!sorted) {
  sorted = true;
  for(var i=0; i < arr.length; i++) {
    if(arr[i] < arr[i-1]) {
      let temp = arr[i];
      arr[i] = arr[i-1];
      arr[i-1] = temp;
      sorted = false;
    }
  }
}

Пример в Java.

public class BubbleSort {
    static void sort(int[] arr) {
        int n = arr.length;
        int temp = 0;
         for(int i=0; i < n; i++){
                 for(int x=1; x < (n-i); x++){
                          if(arr[x-1] > arr[x]){
                                 temp = arr[x-1];
                                 arr[x-1] = arr[x];
                                 arr[x] = temp;
                         }

                 }
         }

    }
    public static void main(String[] args) {

		for(int i=0; i < 15; i++){
			int arr[i] = (int)(Math.random() * 100 + 1);
		}

                System.out.println("array before sorting\n");
                for(int i=0; i < arr.length; i++){
                        System.out.print(arr[i] + " ");
                }
                bubbleSort(arr);
                System.out.println("\n array after sorting\n");
                for(int i=0; i < arr.length; i++){
                        System.out.print(arr[i] + " ");
                }

        }
}

Пример в C ++

// Recursive Implementation
void bubblesort(int arr[], int n)
{
	if(n==1)	//Initial Case
		return;
	bool swap_flag = false;
	for(int i=0;iarr[i+1])
		{
			int temp=arr[i];
			arr[i]=arr[i+1];
			arr[i+1]=temp;
			swap_flag = true;
		}
	}
        // IF no two elements were swapped in the loop, then return, as array is sorted 
	if(swap_flag == false)
		return;
	bubblesort(arr,n-1);	//Recursion for remaining array
}

Пример в Swift

func bubbleSort(_ inputArray: [Int]) -> [Int] {
    guard inputArray.count > 1 else { return inputArray } // make sure our input array has more than 1 element
    var numbers = inputArray // function arguments are constant by default in Swift, so we make a copy
    for i in 0..<(numbers.count - 1) {
        for j in 0..<(numbers.count - i - 1) {
            if numbers[j] > numbers[j + 1] {
                numbers.swapAt(j, j + 1)
            }
        }
    }
    return numbers // return the sorted array
} 

Пример в Python

def bubbleSort(arr): 
    n = len(arr) 
    for i in range(n):
        for j in range(0, n-i-1):
                if arr[j] > arr[j+1] : 
                        arr[j], arr[j+1] = arr[j+1], arr[j]
    print(arr)

Пример в PHP.

function bubble_sort($arr) {
    $size = count($arr)-1;
    for ($i=0; $i<$size; $i++) {
        for ($j=0; $j<$size-$i; $j++) {
            $k = $j+1;
            if ($arr[$k] < $arr[$j]) {
                // Swap elements at indices: $j, $k
                list($arr[$j], $arr[$k]) = array($arr[$k], $arr[$j]);
            }
        }
    }
    return $arr;// return the sorted array
}

$arr = array(1,3,2,8,5,7,4,0);
print("Before sorting");
print_r($arr);

$arr = bubble_sort($arr);
print("After sorting by using bubble sort");
print_r($arr);

Пример в C.

#include 

int BubbleSort(int array[], int n);

int main(void) {
  int arr[] = {10, 2, 3, 1, 4, 5, 8, 9, 7, 6};
  BubbleSort(arr, 10);

  for (int i = 0; i < 10; i++) {
    printf("%d", arr[i]);
  }
  return 0;
}
int BubbleSort(int array[], n)
{
for (int i = 0 ; i < n - 1; i++)
  {
    for (int j = 0 ; j < n - i - 1; j++)     //n is length of array
    {
      if (array[j] > array[j+1])      // For decreasing order use 
      {
        int swap   = array[j];
        array[j]   = array[j+1];
        array[j+1] = swap;
      }
    }
  }
}

Быстрый сортировка

Quick Sort – это эффективный алгоритм сортировки и завоевания. Среднее количество времени Сложность быстрой сортировки – это o (nlog (n)) с наихудшим временем Сложность o (n ^ 2) в зависимости от выбора поворотного элемента, который делит текущий массив в две подпадные массивы.

Например, момент сложности быстрой сортировки примерно O (nlog (n)) Когда выбор поворота делит оригинальный массив на два почти равных дополнительных массива.

С другой стороны, если алгоритм, который выбирает поворотную элемент входных массивов, последовательно выводит 2 подразделения с большим разницей в размерах массива, алгоритм быстрого сорта может достичь наихудшего случая сложности времени O (N ^ 2 ).

Шаги, участвующие в быстрых сортировках:

  • Выберите элемент, чтобы служить поворотом, в данном случае последний элемент массива является поворотом.
  • Разделение: сортировать массив таким образом, чтобы все элементы меньше, чем у поворота налево, и все элементы, превышающие повороты, находятся вправо.
  • Вызовите Quicksort рекурсивно, принимая во внимание предыдущий поворот для правильного подразделения левой и правильной массивы. (Более подробное объяснение можно найти в комментариях ниже)

Пример реализации на разных языках

Реализация в JavaScript:

const arr = [6, 2, 5, 3, 8, 7, 1, 4];

const quickSort = (arr, start, end) => {

  if(start < end) {

    // You can learn about how the pivot value is derived in the comments below
    let pivot = partition(arr, start, end);

    // Make sure to read the below comments to understand why pivot - 1 and pivot + 1 are used
    // These are the recursive calls to quickSort
    quickSort(arr, start, pivot - 1);
    quickSort(arr, pivot + 1, end);
  } 

}

const partition = (arr, start, end) => { 
  let pivot = end;
  // Set i to start - 1 so that it can access the first index in the event that the value at arr[0] is greater than arr[pivot]
  // Succeeding comments will expound upon the above comment
  let i = start - 1,
      j = start;

  // Increment j up to the index preceding the pivot
  while (j < pivot) {

    // If the value is greater than the pivot increment j
    if (arr[j] > arr[pivot]) {
      j++;
    }

    // When the value at arr[j] is less than the pivot:
    // increment i (arr[i] will be a value greater than arr[pivot]) and swap the value at arr[i] and arr[j]
    else {
      i++;
      swap(arr, j, i);
      j++;
    }

  }

  //The value at arr[i + 1] will be greater than the value of arr[pivot]
  swap(arr, i + 1, pivot);

  //You return i + 1, as the values to the left of it are less than arr[i+1], and values to the right are greater than arr[i + 1]
  // As such, when the recursive quicksorts are called, the new sub arrays will not include this the previously used pivot value
  return i + 1;
}

const swap = (arr, firstIndex, secondIndex) => {
  let temp = arr[firstIndex];
  arr[firstIndex] = arr[secondIndex];
  arr[secondIndex] = temp;
}

quickSort(arr, 0, arr.length - 1);
console.log(arr);

Реализация в C.

#include  
void swap(int* a, int* b) 
{ 
    int t = *a; 
    *a = *b; 
    *b = t; 
}
int partition (int arr[], int low, int high) 
{ 
    int pivot = arr[high];     
    int i = (low - 1);  
  
    for (int j = low; j <= high- 1; j++) 
    { 
        if (arr[j] <= pivot) 
        { 
            i++;    
            swap(&arr[i], &arr[j]); 
        } 
    } 
    swap(&arr[i + 1], &arr[high]); 
    return (i + 1); 
}
void quickSort(int arr[], int low, int high) 
{ 
    if (low < high) 
    {
        int pi = partition(arr, low, high); 
  
        quickSort(arr, low, pi - 1); 
        quickSort(arr, pi + 1, high); 
    } 
} 
  

void printArray(int arr[], int size) 
{ 
    int i; 
    for (i=0; i < size; i++) 
        printf("%d ", arr[i]); 
    printf("n"); 
} 
  

int main() 
{ 
    int arr[] = {10, 7, 8, 9, 1, 5}; 
    int n = sizeof(arr)/sizeof(arr[0]); 
    quickSort(arr, 0, n-1); 
    printf("Sorted array: n"); 
    printArray(arr, n); 
    return 0; 
} 

Реализация в Python3.

import random

z=[random.randint(0,100) for i in range(0,20)]

def quicksort(z):
    if(len(z)>1):        
        piv=int(len(z)/2)
        val=z[piv]
        lft=[i for i in z if ival]

        res=quicksort(lft)+mid+quicksort(rgt)
        return res
    else:
        return z
        
ans1=quicksort(z)
print(ans1)

Реализация в Матлаб

a = [9,4,7,3,8,5,1,6,2];

sorted = quicksort(a,1,length(a));

function [unsorted] =  quicksort(unsorted, low, high)
    if low < high
        [pInd, unsorted] = partition(unsorted, low, high);
        unsorted = quicksort(unsorted, low, pInd-1);
        unsorted = quicksort(unsorted, pInd+1, high);
    end

end

function [pInd, unsorted] = partition(unsorted, low, high)
    i = low-1;
    for j = low:1:high-1
        if unsorted(j) <= unsorted(high)
            i = i+1;
            unsorted([i,j]) = unsorted([j,i]);
            
        end
    end
    unsorted([i+1,high]) = unsorted([high,i+1]);
    pInd = i+1;

end

Космическая сложность быстрой сортировки – O (n) Отказ Это улучшение по сравнению с другими алгоритмами сортировки и завоевания, которые занимают O (nlong (n)) космос.

Быстрый сортировки достигают это, изменив порядок элементов в данном массиве. Сравните это с Сортировка слияния алгоритм, который создает 2 массива, каждую длину N/2 в каждой функции вызов.

Однако существует проблема этого сортировки алгоритма времени …| O (n * n) Если поворот всегда хранится в середине. Это может быть преодолено, используя случайную поворот

Сложность

Лучшее, среднее, худшее, память: n log (n) n log (n) n 2log (n). Это не стабильный алгоритм, и QuickSort обычно делается на месте с O (Log (N)) пространством стека.

Космическая сложность быстрой сортировки является O (n). Это улучшение по сравнению с другими алгоритмами сортировки и завоеваний, которые принимают O (n log (n)) пространство.

Тима

Timsort – это быстрый алгоритм сортировки, работающий на устойчивом сложности O (n log (n)).

Timsort – это смесь ввода-сортировки и Mergesort. Этот алгоритм реализован в массивах Java.sort (), а также отсортирован () и сортировка Python (). Меньшие части сортируются с использованием сортировки вставки и позже объединены вместе с помощью Mergesort.

Быстрая реализация в Python:

def binary_search(the_array, item, start, end):
    if start == end:
        if the_array[start] > item:
            return start
        else:
            return start + 1
    if start > end:
        return start

    mid = round((start + end)/ 2)

    if the_array[mid] < item:
        return binary_search(the_array, item, mid + 1, end)

    elif the_array[mid] > item:
        return binary_search(the_array, item, start, mid - 1)

    else:
        return mid

"""
Insertion sort that timsort uses if the array size is small or if
the size of the "run" is small
"""
def insertion_sort(the_array):
    l = len(the_array)
    for index in range(1, l):
        value = the_array[index]
        pos = binary_search(the_array, value, 0, index - 1)
        the_array = the_array[:pos] + [value] + the_array[pos:index] + the_array[index+1:]
    return the_array

def merge(left, right):
    """Takes two sorted lists and returns a single sorted list by comparing the
    elements one at a time.
    [1, 2, 3, 4, 5, 6]
    """
    if not left:
        return right
    if not right:
        return left
    if left[0] < right[0]:
        return [left[0]] + merge(left[1:], right)
    return [right[0]] + merge(left, right[1:])

def timsort(the_array):
    runs, sorted_runs = [], []
    length = len(the_array)
    new_run = [the_array[0]]

    # for every i in the range of 1 to length of array
    for i in range(1, length):
        # if i is at the end of the list
        if i == length - 1:
            new_run.append(the_array[i])
            runs.append(new_run)
            break
        # if the i'th element of the array is less than the one before it
        if the_array[i] < the_array[i-1]:
            # if new_run is set to None (NULL)
            if not new_run:
                runs.append([the_array[i]])
                new_run.append(the_array[i])
            else:
                runs.append(new_run)
                new_run = []
        # else if its equal to or more than
        else:
            new_run.append(the_array[i])

    # for every item in runs, append it using insertion sort
    for item in runs:
        sorted_runs.append(insertion_sort(item))
    
    # for every run in sorted_runs, merge them
    sorted_array = []
    for run in sorted_runs:
        sorted_array = merge(sorted_array, run)

    print(sorted_array)

timsort([2, 3, 1, 5, 6, 7])

Сложность:

Tim Sort имеет стабильную сложность O (N log (n)) и очень хорошо сравнивается с Quicksort. Сравнение сложностей можно найти на этом Диаграмма Отказ

Сортировка слиянием

Сортировка слияния это Разделить и завоевать алгоритм. Он делит входной массив в двух половинках, называет себя для двух половинок, а затем объединяет две сортированные половинки. Основная часть алгоритма задана две сортированные массивы, и мы должны объединить их в единый отсортированный массив. Весь процесс сортировки массива n целых чисел можно обобщить на три шага

  • Разделите массив на две половины.
  • Сортировать левую половину и правую половину, используя тот же повторяющийся алгоритм.
  • Объединить отсортированные половинки.

Есть что известно как Два алгоритма пальца Это помогает нам объединить две сортированные массивы вместе. Используя этот подпрограмму и вызова функции сортировки слияния на половинах массива рекурсивно даст нам окончательный отсортированный массив, который мы ищем.

Поскольку это рекурсионный алгоритм, у нас есть рецидива для него. Соотношение рецидива – это просто способ представлять проблему с точки зрения его подпор.

T (n) * t (n/2) + o (n)

Положив его в обычный английский, мы нарушаем подгруппу на две части на каждом шагу, и у нас есть некоторая линейная работа, которую мы должны сделать для объединения двух отсортированных половинок вместе на каждом шаге.

Сложность

Самое большое преимущество использования сорта слияния в том, что Сложность времени только n * log (n), чтобы сортировать весь массив. Это намного лучше, чем N ^ 2 время работы пузырькового сорта или вставки.

Перед тем, как мы пишем код, давайте понять, как сортировка слияния работает с помощью диаграммы.

  • Первоначально у нас есть массив из 6 несортированных целых чисел ARR (5, 8, 3, 9, 1, 2)
  • Мы разделяем массив на две половины ARR1 = (5, 8, 3) и ARR2 = (9, 1, 2).
  • Опять же, мы разделяем их на две половины: Arr3 = (5, 8) и Arr4 = (3) и Arr5 = (9, 1) и Arr6 = (2)
  • Опять же, мы разделяем их на две половины: Arr7 = (5), ARR8 = (8), ARR9 = (9), ARR10 = (1) и ARR6 = (2)
  • Теперь мы сравним элементы в этих дополнительных массивах, чтобы объединить их.

Характеристики:

  • Космическая сложность: O (n)
  • Сложность времени: o (n * log (n)). Сложность времени для сорта слияния может быть не очевидна с первого взгляда. Коэффициент журнала (N), который приходит в том, что связано с рецидивом, которые мы упомянули ранее.
  • Сортировка на месте: нет в типичной реализации
  • Стабильно: Да
  • Параллелизуемость: Да (несколько параллельных вариантов обсуждаются в третьем издании Кормена, Лейсерсон, кишечника и введения в алгоритмы Штена.)

Реализация C ++

void merge(int array[], int left, int mid, int right)
{
    int i, j, k;

    // Size of left sublist
int size_left = mid - left + 1;

// Size of right sublist
int size_right =  right - mid;

/* create temp arrays */
int Left[size_left], Right[size_right];

/* Copy data to temp arrays L[] and R[] */
for(i = 0; i < size_left; i++)
{
    Left[i] = array[left+i];
}

for(j = 0; j < size_right; j++)
{
    Right[j] = array[mid+1+j];
}

// Merge the temp arrays back into arr[left..right]
i = 0; // Initial index of left subarray
j = 0; // Initial index of right subarray
k = left; // Initial index of merged subarray

while (i < size_left && j < size_right)
{
    if (Left[i] <= Right[j])
    {
        array[k] = Left[i];
        i++;
    }
    else
    {
        array[k] = Right[j];
        j++;
    }
    k++;
}

// Copy the remaining elements of Left[]
while (i < size_left)
{
    array[k] = Left[i];
    i++;
    k++;
}

// Copy the rest elements of R[]
while (j < size_right)
{
    array[k] = Right[j];
    j++;
    k++;
}
}

void mergeSort(int array[], int left, int right)
{
    if(left < right)
    {
        int mid = (left+right)/2;

        // Sort first and second halves
    mergeSort(array, left, mid);
    mergeSort(array, mid+1, right);

    // Finally merge them
    merge(array, left, mid, right);
}
}

Реализация JavaScript

function mergeSort (arr) {
  if (arr.length < 2) return arr;
  var mid = Math.floor(arr.length /2);
  var subLeft = mergeSort(arr.slice(0,mid));
  var subRight = mergeSort(arr.slice(mid));
  return merge(subLeft, subRight);
}

Сначала мы проверяем длину массива. Если это 1, то мы просто вернем массив. Это будет наш базовый случай. Иначе мы узнаем среднее значение и разделите массив на две половины. Теперь мы сортируем оба половинки с рекурсивными вызовами функцией Mergeort.

function merge (a,b) {
    var result = [];
    while (a.length >0 && b.length >0)
        result.push(a[0] < b[0]? a.shift() : b.shift());
    return result.concat(a.length? a : b);
}

Когда мы объединяем две половины, мы храним результат в дополнительной массиве. Мы будем сравнивать начальный элемент левого массива в начальный элемент правого массива. В зависимости от того, что меньше будет выталкиваться в массив результатов, и мы удалим его от существующих массивов, используя оператор Shift (). Если мы все еще окажемся со значениями в левом или правом массиве, мы просто объединяем его в конце результата. Вот отсортированный результат:

var test = [5,6,7,3,1,3,15];
console.log(mergeSort(test));

>> [1, 3, 3, 5, 6, 7, 15]

Учебное пособие по сортировке Merge YouTube

Вот хорошее видео YouTube, которое Прогулки по теме подробно Отказ

Реализация в JS.

const list = [23, 4, 42, 15, 16, 8, 3]

const mergeSort = (list) =>{
  if(list.length <= 1) return list;
  const middle = list.length / 2 ;
  const left = list.slice(0, middle);
  const right = list.slice(middle, list.length);
  return merge(mergeSort(left), mergeSort(right));
}

const merge = (left, right) => {
  var result = [];
  while(left.length || right.length) {
    if(left.length && right.length) {
      if(left[0] < right[0]) {
        result.push(left.shift())
      } else {
        result.push(right.shift())
      }
    } else if(left.length) {
        result.push(left.shift())
      } else {
        result.push(right.shift())
      }
    }
  return result;
}

console.log(mergeSort(list)) // [ 3, 4, 8, 15, 16, 23, 42 ]

Реализация в C.

#include 
#include
void merge(int arr[], int l, int m, int r) 
{ 
    int i, j, k; 
    int n1 = m - l + 1; 
    int n2 =  r - m; 
  
    
    int L[n1], R[n2]; 
  
    for (i = 0; i < n1; i++) 
        L[i] = arr[l + i]; 
    for (j = 0; j < n2; j++) 
        R[j] = arr[m + 1+ j];
    i = 0; 
    j = 0; 
    k = l; 
    while (i < n1 && j < n2) 
    { 
        if (L[i] <= R[j]) 
        { 
            arr[k] = L[i]; 
            i++; 
        } 
        else
        { 
            arr[k] = R[j]; 
            j++; 
        } 
        k++; 
    } 
  
    
    while (i < n1) 
    { 
        arr[k] = L[i]; 
        i++; 
        k++; 
    } 
  
    while (j < n2) 
    { 
        arr[k] = R[j]; 
        j++; 
        k++; 
    } 
} 
  
void mergeSort(int arr[], int l, int r) 
{ 
    if (l < r) 
    {  
        int m = l+(r-l)/2; 
  
        
        mergeSort(arr, l, m); 
        mergeSort(arr, m+1, r); 
  
        merge(arr, l, m, r); 
    } 
}
void printArray(int A[], int size) 
{ 
    int i; 
    for (i=0; i < size; i++) 
        printf("%d ", A[i]); 
    printf("\n"); 
} 
int main() 
{ 
    int arr[] = {12, 11, 13, 5, 6, 7}; 
    int arr_size = sizeof(arr)/sizeof(arr[0]); 
  
    printf("Given array is \n"); 
    printArray(arr, arr_size); 
  
    mergeSort(arr, 0, arr_size - 1); 
  
    printf("\nSorted array is \n"); 
    printArray(arr, arr_size); 
    return 0; 

Реализация в C ++

Рассмотрим массив A = {2,5,7,8,9,12,13} и массив B = {3,5,6,9,15}, и мы хотим, чтобы массив C находиться в порядке возрастания.

void mergesort(int A[],int size_a,int B[],int size_b,int C[])
{
     int token_a,token_b,token_c;
     for(token_a=0, token_b=0, token_c=0; token_a

Реализация в Python

def merge(left,right,compare):
	result = [] 
	i,j = 0,0
	while (i < len(left) and j < len(right)):
		if compare(left[i],right[j]):
			result.append(left[i])
			i += 1
		else:
			result.append(right[j])
			j += 1
	while (i < len(left)):
		result.append(left[i])
		i += 1
	while (j < len(right)):
		result.append(right[j])
		j += 1
	return result

def merge_sort(arr, compare = lambda x, y: x < y):
     #Used lambda function to sort array in both(increasing and decresing) order.
     #By default it sorts array in increasing order
	if len(arr) < 2:
		return arr[:]
	else:
		middle = len(arr) // 2
		left = merge_sort(arr[:middle], compare)
		right = merge_sort(arr[middle:], compare)
		return merge(left, right, compare) 

arr = [2,1,4,5,3]
print(merge_sort(arr))

Реализация в Java

public class mergesort {

	public static int[] mergesort(int[] arr,int lo,int hi) {
		
		if(lo==hi) {
			int[] ba=new int[1];
			ba[0]=arr[lo];
			return ba;
		}
		
		int mid=(lo+hi)/2;
		int arr1[]=mergesort(arr,lo,mid);
		int arr2[]=mergesort(arr,mid+1,hi);
		return merge(arr1,arr2);
	}
	
	public static int[] merge(int[] arr1,int[] arr2) {
		int i=0,j=0,k=0;
		int n=arr1.length;
		int m=arr2.length;
		int[] arr3=new int[m+n];
		while(i

Пример в Java

public class mergesort {
  public static int[] mergesort(int[] arr, int lo, int hi) {
    if (lo == hi) {
      int[] ba = new int[1];
      ba[0] = arr[lo];
      return ba;
    }
    int mid = (lo + hi) / 2;
    int arr1[] = mergesort(arr, lo, mid);
    int arr2[] = mergesort(arr, mid + 1, hi);
    return merge(arr1, arr2);
  }

  public static int[] merge(int[] arr1, int[] arr2) {
    int i = 0, j = 0, k = 0;
    int n = arr1.length;
    int m = arr2.length;
    int[] arr3 = new int[m + n];
    while (i < n && j < m) {
      if (arr1[i] < arr2[j]) {
        arr3[k] = arr1[i];
        i++;
      } else {
        arr3[k] = arr2[j];
        j++;
      }
      k++;
    }
    while (i < n) {
      arr3[k] = arr1[i];
      i++;
      k++;
    }
    while (j < m) {
      arr3[k] = arr2[j];
      j++;
      k++;
    }
    return arr3;
  }

  public static void main(String[] args) {
    int arr[] = {2, 9, 8, 3, 6, 4, 10, 7};
    int[] so = mergesort(arr, 0, arr.length - 1);
    for (int i = 0; i < arr.length; i++)
      System.out.print(so[i] + " ");
  }
}