Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Структуры и алгоритмы / Сортировка_Основные алгоритмы.doc
Скачиваний:
113
Добавлен:
23.02.2015
Размер:
679.94 Кб
Скачать
    1. Улучшенная сортировка простыми вставками

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

3

4

2

1

0

7

9

min

4

2

1

0

7

9

Рис. 9

Тогда при j = 0будет заведомо верноa[0] <= x. Цикл остановится на нулевом элементе, что и было целью условияj >= 0.

Таким образом, сортировка будет происходить правильным образом, а во внутреннем цикле станет на одно сравнение меньше. С учетом того, что оно производилось О(n2)раз, это - реальное преимущество. Однако отсортированный массив будет неполон, так как из него исчезло первое число. Для окончания сортировки это число следует вернуть назад, а затем вставить в отсортированную последовательностьa[1]...a[n] (рис. 10).

min

0

1

2

4

7

9

3

0

1

2

4

7

9

0

1

2

3

4

7

9

Рис. 10

// сортировка вставками со сторожевым элементом

template<class T>

inline void insertSortGuarded(T a[], long size) {

T x;

long i, j;

T backup = a[0]; // сохранить старый первый элемент

setMin(a[0]); // заменить на минимальный

for ( i=1; i < size; i++) { // отсортировать массив

x = a[i];

for ( j=i-1; a[j] > x; j--)

a[j+1] = a[j];

a[j+1] = x;

}

for(j=1;j<size&&a[j] <backup;j++) // вставить backup на правильное место

a[j-1] = a[j];

a[j-1] = backup; // вставка элемента

}

Функция setmin(T& x)должна быть создана пользователем. Она заменяетxна элемент, заведомо меньший (меньший или равный, если говорить точнее) всех элементов массива.

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

Сортировка Шелла является довольно интересной модификацией алгоритма сортировки простыми вставками.

Рассмотрим следующий алгоритм сортировки массива a[0].. a[15] (рис. 11).

12

8

14

6

4

9

1

8

13

5

11

3

18

3

10

9

Рис. 11

1. Вначале сортируем простыми вставками каждые 8 групп из двух элементов (a[0], a[8]), (a[1], a[9]), ... , (a[7], a[15]) (рис.12).

12

5

11

3

4

3

1

8

13

8

14

6

18

9

10

9

Рис. 12

2. Потом сортируем каждую из четырех групп по четыре элемента (a[0], a[4], a[8], a[12]), ..., (a[3], a[7], a[11], a[15]) (рис. 13).

Номер группы

0

1

2

3

0

1

2

3

0

1

2

3

0

1

2

3

4

3

1

3

12

5

10

6

13

8

11

8

18

9

14

9

Рис.13

В нулевой группе будут элементы 4, 12, 13, 18, в первой - 3, 5, 8, 9 и т.п.

3. Далее сортируем 2 группы по 8 элементов, начиная с (a[0], a[2], a[4], a[6], a[8], a[10], a[12], a[14])(рис. 14).

1

3

4

3

10

5

11

6

12

8

13

8

14

9

18

9

Рис. 14

4. В конце сортируем вставками все 16 элементов (рис. 15).

1

3

3

4

5

6

8

8

9

9

10

11

12

13

14

18

Рис. 15

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

Они продвигают элементы максимально близко к соответствующим позициям, так что в последней стадии число перемещений будет весьма невелико. Последовательность и так почти отсортирована. Ускорение подтверждено многочисленными исследованиями и на практике оказывается довольно существенным.

Единственной характеристикой сортировки Шелла является приращение- расстояние между сортируемыми элементами, в зависимости от прохода. В конце приращение всегда равно единице - метод завершается обычной сортировкой вставками, но именно последовательность приращений определяет рост эффективности.

Использованный в примере набор - 8, 4, 2, 1 – является обычным выбором в тех случаях, когда количество элементов представляет собой степень двойки. Однако гораздо лучший вариант предложил Р.Седжвик. Его последовательность имеет вид:

inc[s]=

9*2s-9*2s/2+1, если s четно

8*2s-6*2(s+1)/2+1, если s нечетно

При использовании таких приращений среднее количество операций: O(n7/6),в худшем случае - порядкаO(n4/3).

Обратим внимание на то, что последовательность вычисляется в порядке, противоположном используемому: inc[0] = 1, inc[1] = 5,... Формула дает сначала меньшие числа, затем все большие и большие, в то время как расстояние между сортируемыми элементами, наоборот, должно уменьшаться. Поэтому массив приращенийincвычисляется перед запуском собственно сортировки до максимального расстояния между элементами, которое будет первым шагом в сортировке Шелла. Потом его значения используются в обратном порядке. При использовании формулы Седжвика следует остановиться на значенииinc[s-1],если3*inc[s] > size.

int increment(long inc[], long 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;

} while(3*inc[s] < size);

return s > 0 ? --s : 0;

}

template<class T>

void shellSort(T a[], long size) {

long inc, i, j, seq[40];

int s;

// вычисление последовательности приращений

s = increment(seq, size);

while (s >= 0) {

// сортировка вставками с инкрементами inc[]

inc = seq[s--];

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];

a[j+inc] = temp;

}

}

}

Часто вместо вычисления последовательности во время каждого запуска процедуры, ее значения рассчитывают заранее и записывают в таблицу, которой пользуются, выбирая начальное приращение по тому же правилу: начинаем с inc[s-1],если3*inc[s] > size.