Муниципальное бюджетное общеобразовательное учреждение Гимназия № 16 «Французская»
СОРТИРОВКА МАССИВА
Учитель:
Попова Людмила Валерьевна
Под сортировкой последовательности понимают процесс перестановки элементов последовательности в определенном порядке: по возрастанию, убыванию, последней цифре, сумме делителей, … .
Под сортировкой последовательности понимают процесс перестановки элементов последовательности в определенном порядке: по возрастанию, убыванию, последней цифре, сумме делителей, … .
Пусть дана последовательность элементов a 1 , a 2 , ... , a n .
Элементы этой последовательности – данные произвольного типа, на котором определено отношение порядка “ ” (меньше) такое, что любые два различных элемента сравнимы между собой.
Сортировка означает перестановку элементов последовательности a k1 , a k2 , ... , a kn такую, что
a k 1 a k 2 a kn .
Основными требованиями к программе сортировки массива являются эффективность по времени и экономное использование памяти.
Это означает, что алгоритм не должен использовать дополнительных массивов и пересылок из массива a в эти массивы.
Постановка задачи сортировки в общем виде предполагает, что существуют только два типа действий с данными сортируемого типа:
- сравнение двух элементов ( x y ) и пересылка элемента ( x := y ).
- сравнение двух элементов ( x y )
- и пересылка элемента ( x := y ).
Поэтому удобная мера сложности алгоритма сортировки массива a[1..n] :
- по времени – количество сравнений C (n) и количество пересылок M ( n ) .
- по времени – количество сравнений C (n)
- и количество пересылок M ( n ) .
Простые сортировки
К простым внутренним сортировкам относят методы, сложность которых пропорциональна квадрату размерности входных данных.
Иными словами, при сортировке массива, состоящего из N компонент, такие алгоритмы будут выполнять С * N 2 действий, где С - некоторая константа. Этот факт принято обозначать следующей символикой: O ( N 2 ).
Сортировка
Алгоритмы:
- простые и понятные, но неэффективные для больших массивов
- простые и понятные, но неэффективные для больших массивов
- метод пузырька метод выбора Метод вставка
- метод пузырька метод выбора Метод вставка
- метод пузырька
- метод выбора
- Метод вставка
- сложные, но эффективные
- сложные, но эффективные
- «быстрая сортировка» ( Quick Sort ) сортировка «кучей» ( Heap Sort ) сортировка слиянием пирамидальная сортировка
- «быстрая сортировка» ( Quick Sort ) сортировка «кучей» ( Heap Sort ) сортировка слиянием пирамидальная сортировка
- «быстрая сортировка» ( Quick Sort )
- сортировка «кучей» ( Heap Sort )
- сортировка слиянием
- пирамидальная сортировка
сложность O( N 2 )
сложность O( N · log N )
O( N 2 )
время
O( N · log N )
N
5
5
При сортировке вставками из массива берется очередной элемент и движется вдоль массива к его началу до тех пор, пока не дойдет до большего себя, а затем становится перед этим большим элементом, передвигая участок массива.
Покажем этот метод на примере сортировки массива А(5).
А
индекс элемента
1
2
4
3
3
4
2
1
значение элемента
пример:
4
3
4
4
1
1
1
2
2
2
4
3
1
2
Вынимаем элемент 3. Затем элементы, расположенные выше, сдвигаем вниз – до тех пор , пока не найдем место куда нужно вставить число 3.
1
3
4
2
3
3
3
4
4
4
1
2
2
2
Этот процесс продолжается для числа 1.
1
2
3
4
1
1
3
4
3
4
1
3
4
2
Завершаем сортировку, поместив число 2 на нужное место.
Постановка задачи:
Дано:
неупорядоченный числовой массив;
A – имя массива;
n - размер массива;
i – индекс элемента;
m – элемент массива, с которым работаем (начиная
со второго);
k – индекс элемента, стоящего перед элементом m .
Требуется:
упорядочить числовой массив по возрастанию.
=1) AND a(k)m a(k+1)=a(k) k=k-1 WEND a(k+1)=m NEXT i " width="640"
FOR i = 2 TO n
m=a(i)
k=i-1
WHILE (k=1) AND a(k)m
a(k+1)=a(k)
k=k-1
WEND
a(k+1)=m
NEXT i
МЕТОД В ЖИЗНИ:
В обычной жизни мы сталкиваемся с этим методом при игре в карты. Большинство картежников, сами того не сознавая, пользуются именно таким методом сортировки для упорядочения пришедших им карт. Когда игрок получает очередную карту, все предыдущие уже отсортированы, поэтому он просто вставляет ее в нужное место.
ПРЕИМУЩЕСТВА:
- не требует дополнительной памяти;
- эффективен на небольших наборах данных;
- эффективен на наборах данных, которые уже частично отсортированы;
- это устойчивый алгоритм сортировки (не меняет порядок элементов, которые уже отсортированы);
- может сортировать список по мере его получения.
Основной недостаток :
необходимость многократного сдвига элементов.
Метод пузырька
Идея – пузырек воздуха в стакане воды поднимается со дна вверх.
Для массивов – самый маленький («легкий» элемент перемещается вверх («всплывает»).
1-ый проход
- начиная снизу, сравниваем два соседних элемента; если они стоят «неправильно», меняем их местами
- за 1 проход по массиву один элемент (самый маленький) становится на свое место
5
1
2
3
5
2
1
3
5
1
2
5
1
2
3
3
2-ой проход
3-ий проход
1
2
5
3
1
2
3
5
Для сортировки массива из N элементов нужен N -1 проход (достаточно поставить на свои места N-1 элементов).
1
5
2
3
1
1
2
5
5
2
3
3
A[j+1] then begin c:=A[j]; A[j]:=A[j+1]; A[j+1]:=c; end; 1 2 -ой проход ! A[1] уже на своем месте! 1 2 … N-1 N 1 5 … 3 6 2 for j:=N-1 downto 2 do if A[j] A[j+1] then begin c:=A[j]; A[j]:=A[j+1]; A[j+1]:=c; end; i -ый проход for j:=N-1 downto i do ... i © С.В.Кухта, 2009 " width="640"
Программа
1-ый проход :
сравниваются пары
A[N-1] и A[N], A[N-2] и A[N-1]
…
A[1] и A[2]
A[j] и A[j+1]
1
2
…
N-1
N
5
2
…
6
3
for j:=N-1 downto 1 do
if A[j] A[j+1] then begin
c:=A[j]; A[j]:=A[j+1]; A[j+1]:=c;
end;
1
2 -ой проход
!
A[1] уже на своем месте!
1
2
…
N-1
N
1
5
…
3
6
2
for j:=N-1 downto 2 do
if A[j] A[j+1] then begin
c:=A[j]; A[j]:=A[j+1]; A[j+1]:=c;
end;
i -ый проход
for j:=N-1 downto i do
...
i
© С.В.Кухта, 2009
A[j+1] then begin с := A[j]; A[j] := A[j+1]; A[j+1] := с ; end; end; i © С.В.Кухта, 2009 16 16 " width="640"
Программа
program qq;
const N = 10;
var A: array[1..N] of integer;
i, j, c: integer;
begin
{ заполнить массив }
{ вывести исходный массив }
{ вывести полученный массив }
end.
?
Почему цикл по i до N-1 ?
элементы выше A[i] уже поставлены
for i:=1 to N-1 do begin
for j:=N-1 downto i do
if A[j] A[j+1] then begin
с := A[j];
A[j] := A[j+1];
A[j+1] := с ;
end;
end;
i
© С.В.Кухта, 2009
16
16
16
Эффективность метода пузырька
Внешний цикл выполнился n –1 раз. Внутренний цикл выполняется j раз ( K = n –2, n –1, ..., 1 ).
Каждое выполнение тела внутреннего цикла заключается в одном сравнении и, возможно, в одной перестановке. Поэтому
C ( n ) =1+2+ ...+ n –1 = n *( n –1)/2,
M ( n ) = n *( n –1)/2 .
В худшем случае (когда элементы исходного массива расположены в порядке убывания)
C ( n ) = n *( n –1)/2= O ( n 2 ),
M ( n ) = n *( n –1)/2= O ( n 2 ).
16
16
A[j+1] then begin с := A[j]; A[j] := A[j+1]; A[j+1] := с ; flag := True; { поднять флаг } end; until not flag; { выход при flag=False } flag := False; ? Как улучшить ? flag := True; not flag; 18 18 " width="640"
16
Метод пузырька с флажком
Идея – если при выполнении метода пузырька не было обменов, массив уже отсортирован и остальные проходы не нужны.
Реализация: переменная-флаг , показывающая, был ли обмен ; если она равна False , то выход.
2
1
2
1
3
4
4
3
var flag: boolean;
repeat
flag := False; { сбросить флаг }
for j:=N-1 downto 1 do
if A[j] A[j+1] then begin
с := A[j];
A[j] := A[j+1];
A[j+1] := с ;
flag := True; { поднять флаг }
end;
until not flag; { выход при flag=False }
flag := False;
?
Как улучшить ?
flag := True;
not flag;
18
18
A[j+1] then begin с := A[j]; A[j] := A[j+1]; A[j+1] := с ; flag := True; { поднять флаг } end; until not flag; { выход при flag=False } i := 0; i := i + 1; i 18 18 " width="640"
18
Метод пузырька с флажком
i := 0;
repeat
i := i + 1;
flag := False; { сбросить флаг }
for j:=N-1 downto 1 do
if A[j] A[j+1] then begin
с := A[j];
A[j] := A[j+1];
A[j+1] := с ;
flag := True; { поднять флаг }
end;
until not flag; { выход при flag=False }
i := 0;
i := i + 1;
i
18
18
18
Метод выбора
Идея:
- найти минимальный элемент и поставить на первое место (поменять местами с A[1] ) из оставшихся найти минимальный элемент и поставить на второе место (поменять местами с A[ 2 ] ) , и т.д.
- найти минимальный элемент и поставить на первое место (поменять местами с A[1] )
- из оставшихся найти минимальный элемент и поставить на второе место (поменять местами с A[ 2 ] ) , и т.д.
1
2
4
3
4
3
1
2
1
2
3
4
1
3
4
2
Метод выбора
нужно N-1 проходов
for i := 1 to N-1 do begin
nMin = i ;
for j:= i+1 to N do
if A[j]
if nMin i then begin
c:=A[i];
A[i]:=A[nMin];
A[nMin]:=c;
end;
end;
N-1
поиск минимального от A[i] до A[N]
i
N
i+1
если нужно, переставляем
?
Можно ли убрать if ?
21
21
Далее материал для любознательных ))
21
В отличие от простых сортировок, имеющих сложность O( N 2 ) , к улучшенным сортировкам относятся алгоритмы с общей сложностью O( N*logN ) .
Необходимо, однако, отметить, что на небольших наборах сортируемых данных ( N
- выигрыш становится заметным только при больших N .
- выигрыш становится заметным только при больших N .
Следовательно, если необходимо отсортировать маленький набор данных, то выгоднее взять одну из простых сортировок.
Сортировка Шелла
Эта сортировка базируется на уже известном нам алгоритме простых вставок.
Смысл ее состоит в раздельной сортировке методом простых вставок нескольких частей, на которые разбивается исходный массив.
Эти разбиения помогают сократить количество пересылок:
- для того, чтобы освободить "правильное" место для очередного элемента, приходится уже сдвигать меньшее количество элементов.
- для того, чтобы освободить "правильное" место для очередного элемента, приходится уже сдвигать меньшее количество элементов.
Сортировка Шелла
Сортирует элементы массива А[1.. n ] следующим образом:
- на первом шаге упорядочиваются элементы n /2 пар ( A[i], А[ n /2 + i] ) для 1 i n /2 ,
- на втором шаге упорядочиваются элементы в n /4 группах из четырех элементов ( A [ i ], A [ n /4 + i ], A [ n /2 + i ], A [3 n /4 + i ] ) для 1 i n /4 ,
- на третьем шаге упорядочиваются элементы в n /8 группах из восьми элементов и т.д.;
- на последнем шаге упорядочиваются элементы сразу во всем массиве А .
На каждом шаге для упорядочивания элементов используется метод сортировки вставками.
0 do begin for i:=incr+1 to n do begin j:= i-incr; while j0 do if A[j]A[j+incr] then begin c:= A[j]; A[j]:=A[j+incr]; A[j+incr]:=A[j]; j:=j-incr end else j:=0 { останов проверки } end; incr:= incr div 2 end ; " width="640"
Сортировка Шелла
Фрагмент программы:
incr:= n div 2;
while incr0 do begin
for i:=incr+1 to n do begin
j:= i-incr;
while j0 do
if A[j]A[j+incr] then begin
c:= A[j]; A[j]:=A[j+incr];
A[j+incr]:=A[j];
j:=j-incr
end
else j:=0 { останов проверки }
end;
incr:= incr div 2
end ;
Пирамидальная сортировка
Попытаемся теперь усовершенствовать другой рассмотренный выше простой алгоритм: сортировку простым выбором.
Р.Флойд предложил перестроить линейный массив в пирамиду - своеобразное бинарное дерево , - а затем искать минимум только среди тех элементов, которые находятся непосредственно "под" текущим вставляемым.
Пирамидальная сортировка: просеивание
Итак, будем рассматривать наш линейный массив как пирамидальную структуру:
a[1]
a[2]
a[4]
a[8]
a[9]
a[5]
a[10]
a[3]
a[11]
a[6]
a[7]
a[12]
Видно, что любой элемент a[i] ( 1 i N div 2 ) "опирается" на элементы a[ 2 *i] и a[ 2 *i+ 1 ] .
И в каждой такой тройке максимальный элемент должен находится "сверху".
Конечно, исходный массив может и не удовлетворять этому свойству, поэтому его потребуется немного перестроить.
Пирамидальная сортировка: просеивание
a[1]
a[2]
a[4]
a[8]
a[9]
a[5]
a[3]
a[10]
a[6]
a[11]
a[7]
a[12]
Начнем процесс просеивания "снизу". Половина элементов (с ( ( N div 2)+1 )-го по N -й) являются основанием пирамиды, их просеивать не нужно.
А для всех остальных элементов (двигаясь от конца массива к началу) проверяем тройки a[i] , a[ 2 *i] и a[2*i+1] и перемещать максимум "наверх" - в элемент a[i] . При этом, если в результате одного перемещения нарушается пирамидальность в другой (ниже лежащей) тройке элементов, там снова необходимо "навести порядок" - и так до самого "низа" пирамиды.
a[j] then begin x:=a[j]; a[j]:=a[k]; a[k]:=x; j:=k end else break end end; " width="640"
Пирамидальная сортировка: просеивание
Фрагмент программы алгоритма просеивания:
for i:= (N div 2)downto 1 do begin
j:=i;
while j
k:=2*j;
if (k+1
k:=k+1;
if a[k]a[j] then begin
x:=a[j]; a[j]:=a[k]; a[k]:=x;
j:=k end
else break
end
end;
Пирамидальная сортировка: алгоритм
Для того чтобы отсортировать массив методом Пирамиды, необходимо выполнить такую последовательность действий:
0-й шаг: Превратить исходный массив в пирамиду (с помощью просеивания).
1-й шаг: Для N -1 элементов, начиная с последнего, производить следующие действия:
- поменять местами очередной "рабочий" элемент с первым; просеять (новый) первый элемент, не затрагивая, однако, уже отсортированный хвост последовательности (элементы с i -го по N -й).
- поменять местами очередной "рабочий" элемент с первым;
- просеять (новый) первый элемент, не затрагивая, однако, уже отсортированный хвост последовательности (элементы с i -го по N -й).
a[j] then begin x:=a[j]; a[j]:=a[k]; a[k]:= x; j:= k end else flag:=false end end; " width="640"
Пирамидальная сортировка
Часть программы, реализующая нулевой шаг алгоритма, приведена в пункте "Просеивание", поэтому здесь приведена только реализацией основного шага 1:
for i:= N downto 2 do begin
x:=a[1]; a[1]:=a[i]; a[i]:=x;
j:= 1; flag:=true;
while ( j ) and flag do begin
k:=2*j;
if (k+1
k:= k+1;
if a[k]a[j] then begin
x:=a[j]; a[j]:=a[k]; a[k]:= x;
j:= k end
else flag:=false
end
end;
Пирамидальная сортировка
Эффективность алгоритма
Пирамидальная сортировка хорошо работает с большими массивами, однако на маленьких примерах ( N
В среднем этот алгоритм имеет сложность O( N*log N ) .
= X 3 шаг: так же отсортировать две получившиеся области Разделяй и властвуй (англ. divide and conquer ) " width="640"
«Быстрая сортировка» ( Quick Sort )
Идея – более эффективно переставлять элементы, расположенные дальше друг от друга.
?
Сколько перестановок нужно, если массив отсортирован по убыванию, а надо – по возрастанию?
N div 2
1 шаг: выбрать некоторый элемент массива X
2 шаг: переставить элементы так:
при сортировке элементы не покидают « свою область»!
A[i]
A[i] = X
3 шаг: так же отсортировать две получившиеся области
Разделяй и властвуй (англ. divide and conquer )
= X ( должен стоять справа ) уменьшая R , найти первый элемент A[R] , который X ( должен стоять слева ) если L , поменять местами A[L] и A[R] и перейти к п. 3 78 6 82 67 55 44 34 " width="640"
«Быстрая сортировка» ( Quick Sort )
78
6
82
67
55
44
34
?
Как лучше выбрать X ?
Медиана – такое значение X, что слева и справа от него в отсортированном массиве стоит одинаковое число элементов ( для этого надо отсортировать массив… ).
Разделение:
- выбрать средний элемент массива ( X =67 )
- установить L:=1 , R:=N
- увеличивая L , найти первый элемент A[L] , который = X ( должен стоять справа )
- уменьшая R , найти первый элемент A[R] , который X ( должен стоять слева )
- если L , поменять местами A[L] и A[R] и перейти к п. 3
78
6
82
67
55
44
34
R : разделение закончено 36 36 " width="640"
«Быстрая сортировка» ( Quick Sort )
78
6
L
82
67
55
44
34
R
34
6
82
67
L
55
44
78
R
34
6
44
67
55
L
R
82
78
34
6
44
55
R
67
L
82
78
!
L R : разделение закончено
36
36
X do R:= R - 1; if L R then begin c:= A[L]; A[L]:= A[R]; A[R]:= c; L:= L + 1; R:= R - 1; end; end; обмен двигаемся дальше сортируем две части 36 36 " width="640"
36
«Быстрая сортировка» ( Quick Sort )
procedure QSort ( first, last: integer);
var L, R, c, X: integer;
begin
if first
X:= A[(first + last) div 2];
L:= first; R:= last;
QSort(first, R); QSort(L, last);
end;
end.
ограничение рекурсии
разделение
while L
while A[L] X do L:= L + 1;
while A[R] X do R:= R - 1;
if L R then begin
c:= A[L]; A[L]:= A[R]; A[R]:= c;
L:= L + 1; R:= R - 1;
end;
end;
обмен
двигаемся дальше
сортируем две части
36
36
36
«Быстрая сортировка» ( Quick Sort )
program qq;
const N = 10;
var A: array[1..N] of integer;
begin
{ заполнить массив }
{ вывести исходный массив на экран }
Qsort ( 1, N ); { сортировка }
{ вывести результат }
end .
procedure QSort ( first, last: integer);
...
!
Сложность (в среднем) !
38
38
Количество перестановок
(случайные данные)
N
10
QuickSort
100
11
«пузырек»
24
184
200
2263
426
500
9055
1346
1000
3074
63529
248547
?
От чего зависит скорость?
?
Как хуже всего выбирать X ?
СПАСИБО ЗА ВНИМАНИЕ!
(Теперь пройдите тест по изученной теме)