Сортировка Шелла

Материал из Википедии — свободной энциклопедии
Это старая версия этой страницы, сохранённая StarMarine (обсуждение | вклад) в 15:29, 30 апреля 2008 (Литература). Она может серьёзно отличаться от текущей версии.
Перейти к навигации Перейти к поиску

Сортировка Шелла (англ. Shell sort) — алгоритм сортировки, идея которого состоит в сравнении элементов, стоящих не только рядом, но и на расстоянии друг от друга.

При сортировке Шелла сначала сравниваются и сортируются между собой ключи, отстоящие один от другого на некотором расстоянии . После этого процедура повторяется для некоторых меньших значений , а завершается сортировка Шелла упорядочиванием элементов при (то есть, обычной сортировкой вставками. Эффективность сортировки Шелла в определённых случаях обеспечивается тем, что элементы «быстрее» встают на свои места (в простых методах сортировки вставками или пузырьком каждая перестановка двух элементов уменьшает количество инверсий в списке максимум на 1, при сортировке Шелла же это число может быть больше).

Пример


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

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

В полученном списке на втором шаге вновь сортируются подсписки из отстоящих на 3 позиции элементов.

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

Выбор длины промежутков

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

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

Примеры реализаций

Pascal

uses dos;
type t1=array [1..8000] of integer;
Var i: integer;
    A:t1;
    n: integer;
    h,m,s,ds: word;
Procedure swap (Var i,j: integer);
Var ob: longint;
Begin
   ob:=A[i];
   A[i]:=A[j];
   A[j]:=ob;
end;
procedure Bubble(Var step: integer);
Var i:integer;
    f: boolean;
    j,b: integer;
Begin
   for j:=1 to step do
      repeat
      f:=true;
      i:=j;
      while i+step <= n do
         begin
            if A[i]> A[i+ step] then
               begin
                  b:=i+step;
                  swap(i,b);
                  f:=false
               end;
               inc (i,step);
         end;
      until(f);
end;
procedure shelsort (Var steps:t1; Var n: integer);
Var i,k: byte;
Begin
   k:=1;
   steps[1]:=1;
   while steps[k]<=n do
      begin
         inc(k);
         steps[k]:=steps[k-1]*3+1;
      end;
   for i:=k downto 1 do
      bubble (steps[i]);
end;
BEGIN
   randomize;
   writeln ('vvedite kol-vo chisel');
   readln(n);
   writeln (' nash massiv');
   for i:=1 to n do
   A[i]:=random(3000);
      settime (0,0,0,0);
      shelsort (A,n);
      gettime (h,m,s,ds);
      for i:=1 to n do
         write(' ',A[i],' ');
         write (' chasi ',h,' ','minuti ',m,' ',' secundi ',s,' ','dolisecundi ',' ',ds)
END.

Глагол

ЗАДАЧА Шелл(a+: РЯД ИЗ ЦЕЛ);
ПЕР
  b,i,j,k,h: ЦЕЛ;
УКАЗ
  b:=РАЗМЕР(a)-1;
  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 
  КОН
КОН Шелл;

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 =  j-k;
         }
         a[j] = h;
      }
   }
}

Python

  def shellsort(a):
      def new_increment(a):
          i = int(len(a) / 2)
          yield i
          while i != 1:
              if i == 2:
                  i = 1
              else: 
                  i = int(numpy.round(i/2.2))
              yield i
      for increment in new_increment(a):
          for i in xrange(increment, len(a)):
              for j in xrange(i, increment-1, -increment):
                  if a[j - increment] < a[j]:
                      break
                  a[j],a[j - increment] = a[j - increment],a[j]
      return a

PHP

function sort_shell($a)
{
    $n = count($a);
    $step = $n / 2;

    while ($step > 0) 
    {
	for ($i=0; $i<($n-$step); $i++) 
        {
	    $j = $i;
	    while ( ($j >= 0) && ($a[$j] > $a[$j+$step]) ) 
            {
	        $tmp = $a[$j];
		$a[$j] = $a[$j+$step];
		$a[$j+$step] = $tmp;
		$j--;
	    }
	}
	$step /= 2;
    }

    return $a;
}

C++

template <class T>
void Shell_sort( T a[], const int n )
{  
    for(int step = n/2 ; step>0 ; step >>= 1){
	for( int i=0; i<(n-step); ++i ){
	    int j = i;
	    while ( (j>=0) && (a[j]>a[j+step]) ){
		T tmp = a[j];
		a[j] = a[j+step];
		a[j+step] = tmp;
		--j;
	    }
	}
    }
}

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 / 2;
            }
        }
this is more quickly
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;
            }
        }

Ruby

def shllsrt(a)
  d=(a.size-1)/2
  while d>0
    for i in 1..a.size-d
      while i>=1 && a[i]>a[i+d]
       a[i],a[i+d],i=a[i+d],a[i],i-d
      end
    end
    d/=2
  end
  a
end

Ссылки

Дональд Эрвин Кнут. Искусство программирования на ЭВМ. Том 3. Сортировка и поиск, 2-е изд. Гл. 5.2.1. ISBN 5-8459-0082-4