- •1. Методы сортировок
- •2. Метод «пузырька» – метод простого обмена
- •3. Модифицированный «пузырек»
- •4. Шейкер-сортировка
- •5. Сортировка вставками (метод прямого включения)
- •6. Сортировка бинарными вставками
- •7. Сортировка методом простого выбора
- •Методы быстрой сортировки
- •8. Сортировка слияниями
- •Рис 1. Иллюстрация слияния упорядоченных фрагментов.
- •9. Быстрая сортировка (сортировка Хоара, 1962)1
Методы быстрой сортировки
8. Сортировка слияниями
Метод слияний – один из первых в теории алгоритмов сортировки. Он предложен Дж. фон Нейманом в 1945 году.
Рассмотрим следующую задачу. Объединить («слить») упорядоченные фрагменты массива A A[k],...A[m] и A[m+1],..., A[q] в один A[k],..., A[q], естественно, тоже упорядоченный (kmq). Основная идея решения состоит в сравнении очередных элементов каждого фрагмента, выяснении, какой из элементов меньше, переносе его во вспомогательный массив D (для простоты) и продвижении по тому фрагменту массива, из которого взят элемент. При этом следует не забыть записать в D оставшуюся часть того фрагмента, который не успел себя «исчерпать».
Пример. Пусть первый фрагмент состоит из 4-х элементов: 2 4 6 11, а второй – из 8-и: 1 3 5 7 14 17. Рис 1. Иллюстрация слияния упорядоченных фрагментов. иллюстрирует начало объединения упорядоченных фрагментов.

Рис 1. Иллюстрация слияния упорядоченных фрагментов.
Реализация слияния фрагментов:
#include <iostream>
#include <locale.h>
using namespace std;
void Sl(int m[],int k,int q) // или ...int *a
{
// k – нижняя граница упорядоченного фрагмента
// q – верхняя граница фрагмента
int i,j,t,mid,d[20];
i=k;
mid=k+(q-k)/2;
j=mid+1;
t=1;
while (i<=mid && j<=q)
{
if (m[i]<=m[j]) {d[t]=m[i]; i++;}
else { d[t]=m[j]; j++;}
t++;
}
// Один из фрагментов обработан полностью, осталось перенести в D остаток другого фрагмента
while (i<=mid)
{ d[t]=m[i]; i++; t++;}
while (j<=q)
{ d[t]=m[j]; j++; t++;}
for (i=1;i<=t-1;i++) m[k+i-1]=d[i];
}
// Рекурсивная реализация сортировки слиянием
void Sort_Sl(int *m, int i,int j)
{
int t;
if (i<j)
// Обрабатываемый фрагмент массива состоит более, чем из одного элемента
if (j-i==1) {
if (m[j]<m[i])
// Обрабатываемый фрагмент массива состоит из двух элементов*)
{ t=m[i]; m[i]=m[j]; m[j]=t;};}
else {
// Разбиваем заданный фрагмент на два
Sort_Sl(m,i,i+(j-i)/2); // рекурсивные вызовы процедуры Sort_Sl
Sort_Sl(m,i+(j-i)/2+1,j);
Sl(m,i,j);
}
}
void input(int *m, int &n) // Имя массива является адресом его начала.
{ // Или void input(int m[], int &n)
cout<<"Введите количество элементов массива ";
cin>>n;
for (int i=0;i<n;i++)
{
cout<<"a["<<i<<"]=";
cin>>m[i]; // Конструкция a[i] эквивалентна *(a + i)
}
}
void print (int *m, int n)
{
for (int i=0;i<n;i++)
cout<<m[i]<<" ";
cout<<"\n";
}
void main()
{
setlocale(LC_ALL,"rus");
const int nmax=20;
int n, a[nmax];
input(a,n);
cout<<"Исходный массив:\n";
print(a,n);
Sort_Sl(a,0,n-1);
cout<<"Отсортированный массив:\n";
print(a,n);
}
Первый вызов процедуры – Sort_Sl(a,0,n-1);, где a – исходный массив из N элементов.
Данный алгоритм работает быстрее чем, например, пузырьковая, а недостаток метода – в требовании довольно большого объема дополнительной памяти.
9. Быстрая сортировка (сортировка Хоара, 1962)1
Рассмотрим элемент, находящийся посередине массива М (назовем его Х). Затем начнем осуществлять два встречных просмотра массива: двигаемся слева направо, пока не найдем элемент M[i]>X и справа налево, пока не найдем элемент M[j]<X. Когда указанные элементы будут найдены, поменяем их местами и продолжим процесс "встречных просмотров с обменами", пока два идущих навстречу друг другу просмотра не встретятся. В результате массив разделится на две части: левую, состоящую из элементов меньших или равных Х, и правую, в которую попадут элементы, большие или равные Х. После такого разделения массива М, чтобы завершить его сортировку, достаточно осуществить то же самое с обеими полученными частями, затем с частями этих частей и т.д., пока каждая часть не будет содержать ровно один элемент.
Реализация указанного метода приводит к следующему (рекурсивному!) решению задачи сортировки:
void Qsort(int a[],int L,int R) // или ...int *a
{ int i=L,j=R,w,x;
x=a[(L+R)/2];
do { while (a[i]<x) i++;
while (a[j]>x) j--;
if(i<=j)
{w=a[i]; a[i]=a[j]; a[j]=w;
i++; j--;}
} while (i<j);
if (L<j) Qsort(a,L,j);
if (i<R) Qsort(a,i,R);
}
void main()
{
setlocale(LC_ALL,"rus");
const int nmax=20;
int n, a[nmax];
input(a,n);
cout<<"Исходный массив:\n";
print(a,n);
Qsort(a,0,n-1);
cout<<"Отсортированный массив:\n";
print(a,n);
}
Основная программа должна содержать вызов процедуры в виде:
Qsort(a,0,N-1);
1 Идея метода: весь массив делится на две равные части с использованием барьерного элемента. В правой части располагаются элементы, большие барьерного; в левой – меньшие. Если в правой части обнаруживается элемент меньший барьерного, а в левой – больший, то они меняются местами. Далее левую часть снова делим пополам, выбираем барьерный элемент и т.д.
Алгоритм быстрой сортировки удобнее реализовать, если использовать рекурсию.
