Сортировка Шелла (Shell Sort)
- Подробности
- Категория: Сортировка и поиск
В 1959 году американский ученый Дональд Шелл опубликовал алгоритм сортировки, который впоследствии получил его имя – «Сортировка Шелла». Этот алгоритм может рассматриваться и как обобщение пузырьковой сортировки, так и сортировки вставками.
Идея метода заключается в сравнение разделенных на группы элементов последовательности, находящихся друг от друга на некотором расстоянии. Изначально это расстояние равно d или N/2, где N — общее число элементов. На первом шаге каждая группа включает в себя два элемента расположенных друг от друга на расстоянии N/2; они сравниваются между собой, и, в случае необходимости, меняются местами. На последующих шагах также происходят проверка и обмен, но расстояние d сокращается на d/2, и количество групп, соответственно, уменьшается. Постепенно расстояние между элементами уменьшается, и на d=1 проход по массиву происходит в последний раз.
Худшее время |
O(n2) |
---|---|
Лучшее время |
O(n log2 n) |
Среднее время |
зависит от выбранных шагов |
Затраты памяти |
О(n) всего, O(1) дополнительно |
Пример

Пусть дан список и выполняется его сортировка методом Шелла, а в качестве значений
выбраны
.
На первом шаге сортируются подсписки , составленные из всех элементов
, различающихся на 5 позиций, то есть подсписки
,
,
,
,
.
В полученном списке на втором шаге вновь сортируются подсписки из отстоящих на 3 позиции элементов.
Процесс завершается обычной сортировкой вставками получившегося списка.
Реализация алгоритма на различных языках программирования:
Псевдокод
ЗАДАЧА Шелл(a=: РЯД ИЗ ЦЕЛ); ПЕР b,i,j,k,h: ЦЕЛ; УКАЗ b:=РАЗМЕР(a); k:=b ДЕЛИТЬ 2; ПОКА k>0 ВЫП ОТ i:=1 ДО b-k ВЫП j:=i; ПОКА (j>=1) И (a[j]>a[j+k]) ВЫП h:=a[j]; a[j]:=a[j+k]; a[j+k]:=h; УМЕНЬШИТЬ(j); КОН; КОН; k:=k ДЕЛИТЬ 2 КОН КОН Шелл;
Cи
/* Пример из книги Герберта Шилдта */ void shell(char *items, int count) { register int i, j, gap, k; char x, a[5]; a[0]=9; a[1]=5; a[2]=3; a[3]=2; a[4]=1; for(k=0; k < 5; k++) { gap = a[k]; for(i=gap; i < count; ++i) { x = items[i]; for(j=i-gap; (x < items[j]) && (j >= 0); j=j-gap) items[j+gap] = items[j]; items[j+gap] = x; } } }
C++
int increment(long inc[], long size) { // inc[] массив, в который заносятся инкременты // size размерность этого массива int p1, p2, p3, s; p1 = p2 = p3 = 1; s = -1; do {// заполняем массив элементов по формуле Роберта Седжвика if (++s % 2) { inc[s] = 8*p1 - 6*p2 + 1; } else { inc[s] = 9*p1 - 9*p3 + 1; p2 *= 2; p3 *= 2; } p1 *= 2; // заполняем массив, пока текущая инкремента хотя бы в 3 раза меньше количества элементов в массиве } while(3*inc[s] < size); return s > 0 ? --s : 0;// возвращаем количество элементов в массиве } template<class T> void shellSort(T a[], long size) { // inc инкремент, расстояние между элементами сравнения // i и j стандартные переменные цикла // seq[40] массив, в котором хранятся инкременты long inc, i, j, seq[40]; int s;//количество элементов в массиве seq[40] // вычисление последовательности приращений s = increment(seq, size); while (s >= 0) { //извлекаем из массива очередную инкременту inc = seq[s--]; // сортировка вставками с инкрементами inc for (i = inc; i < size; i++) { T temp = a[i]; // сдвигаем элементы до тех пор, пока не дойдем до конца или не упорядочим в нужном порядке for (j = i-inc; (j >= 0) && (a[j] > temp); j -= inc) a[j+inc] = a[j]; // после всех сдвигов ставим на место j+inc элемент, который находился на i месте a[j+inc] = temp; } } }
VBA
Sub Sort(Mus() As Long) Dim i As Long, k As Long, Pos As Long Dim l As Long, r As Long, n As Long, tmp As Long l = LBound(Mus) r = UBound(Mus) n = r - l + 1 k = 1 Do k = k * 3 + 1 Loop Until k > n Do k = k \ 3 For i = (k + l) To r Pos = i tmp = Mus(i) Do While Mus(Pos - k) > tmp Mus(Pos) = Mus(Pos - k) Pos = Pos - k If (Pos - k) < l Then Exit Do Loop Mus(Pos) = tmp Next Loop Until k = 1 End Sub
C#
void shellSort(int[] arr) { int j; int step = arr.Length / 2; while (step > 0) { for (int i = 0; i < (arr.Length - step); i++) { j = i; while ((j >= 0) && (arr[j] > arr[j + step])) { int tmp = arr[j]; arr[j] = arr[j + step]; arr[j + step] = tmp; j-=step; } } step = step / 2; } }
Этот более быстрый
private void shellSort(int[] vector) { int step = vector.Length / 2; while (step > 0) { int i, j; for (i = step; i < vector.Length; i++) { int value = vector[i]; for (j = i - step; (j >= 0) && (vector[j] > value); j -= step) vector[j + step] = vector[j]; vector[j + step] = value; } step /= 2; } }
Java
void sort_shell(int[] a){ int i, j, k, h, m=0, b=a.length; int[] d = { 1, 4, 10, 23, 57, 145, 356, 911, 1968, 4711, 11969, 27901, 84801, 213331, 543749, 1355339, 3501671, 8810089, 21521774, 58548857, 157840433, 410151271, 1131376761, 2147483647 }; while (d[m] < b) ++m; while (--m >= 0){ k = d[m]; for (i = k; i < b; i++){ // k-сортировка j=i; h=a[i]; while ((j >= k) && (a[j-k] > h)){ a[j]=a[j-k]; j -= k; } a[j] = h; } } }
Object Pascal (Delphi)
var incr: array [0..23] of integer = (1, 4, 10, 23, 57, 145, 356, 911, 1968, 4711, 11969, 27901, 84801, 213331, 543749, 1355339, 3501671, 8810089, 21521774, 58548857, 157840433, 410151271, 1131376761, 2147483647); type arInt = array of integer; procedure ShellSort(var Arr: arInt); var C: Boolean; G: Integer; I: Integer; J: Integer; Tmp: Integer; len: Integer; cur_inc: integer; begin len := Length(Arr) - 1; cur_inc := 0; while 3 * incr[cur_inc + 1] <= Length(Arr) do inc(cur_inc); repeat g := incr[cur_inc]; i := g; repeat j := i - g; c := True; repeat if arr[j] >= arr[j + g] then c := False else begin Tmp := Arr[j]; Arr[j] := Arr[j+g]; Arr[j+g] := Tmp; end; dec(j, g); until not ((j >= 0) and C); inc(i); until not (i <= len); dec(cur_inc); until not (cur_inc <> -1); end;
PHP
function ShellSort($elements,$length) { $k=0; $gap[0] = (int) ($length / 2); while($gap[$k] > 1) { $k++; $gap[$k]= (int)($gap[$k-1] / 2); }//end while for($i = 0; $i <= $k; $i++){ $step=$gap[$i]; for($j = $step; $j < $length; $j++) { $temp = $elements[$j]; $p = $j - $step; while($p >= 0 && $temp < $elements[$p]) { $elements[$p + $step] = $elements[$p]; $p= $p - $step; }//end while $elements[$p + $step] = $temp; }//endfor j }//endfor i return $elements; }// end function // Exmaple // $SortedElements=shellsort($UnsortedElements,length of list(an integer)); // e.g: $elements=shellsort($elements,10);
Python
def shell(seq): inc = len(seq) // 2 while inc: for i, el in enumerate(seq): while i >= inc and seq[i - inc] > el: seq[i] = seq[i - inc] i -= inc seq[i] = el inc = 1 if inc == 2 else int(inc * 5.0 / 11) data = [22, 7, 2, -5, 8, 4] shell(data) print data # [-5, 2, 4, 7, 8, 22]
Ruby
n = mass.size - 1 d = n/2 while d >= 1 n.downto(0) do |i| 0.upto(i-d) do |j| mass[j], mass[j+d] = mass[j+d], mass[j] if mass[j] > mass[j+d] end end d /= 2 end puts mass
Perl
@out=(5,3,7,9,2,1,6,5,3,7,9,3,4); for(my$k=int($N/2);$k>0;$k=int($k/2)){ for(0..$#out-$k){ $j=$_; while($j>=0&&$out[$j]>$out[$j+$k]){ ($out[$j],$out[$j+$k])=($out[$j+$k],$out[$j]); $j--; } } }
Невзирая на то, что сортировка Шелла во многих случаях медленнее, чем быстрая сортировка, она имеет ряд преимуществ:
- отсутствие потребности в памяти под стек;
- отсутствие деградации при неудачных наборах данных — быстрая сортировка легко деградирует до O(n?), что хуже, чем худшее гарантированное время для сортировки Шелла.
При написании статьи были использованы открытые источники сети интернет :