8. Сортиране чрез сравнение и трансформация
Сортиране -
общи положения; сортиране чрез сравнение [wiki]
** Класификации на алгоритмите за сортиране [Глава 3, стр.187]
* В зависимост от местонахождението на данните:
- вътрешно (директен достъп), например бързо сортиране и
- външно (последователен достъп), например сливане.
* В зависимост от операцията:
- чрез сравнение (<,
> и ==) на двойки елементи и
- чрез трансформация, напр. сортиране чрез броене.
* Свойство на алгоритъма за сортиране:
- устойчиви - относителният ред на елементите с равни
ключове остава непроменен и
- неустойчиви - разместване на елементи с равни ключове
(сортиране с 2 ключа)
* Ефективност на алгоритмите за сортиране - брой на извършени
сравнения и размени (присвоявания).
* Използване на допълнителна памет.
** Дърво на сравненията; сортиране на 3 числа.
** Класически универсални "елементарни" методи за сортиране чрез
сравнение O(n2):
- пряко вмъкване - намираме елемент, който "не е сортиран" и
го поставяме на мястото му в сортираната част;
- пряка селекция (избор) - намираме най-малкия елемент и го
поставяме на мястото му в окончателно сортираната част;
- мехурчето - последователно се разглеждат двойки елементи и
евентуално се разменят.
** Бързо сортиране на Хоор - O(n log2n) средно и O(n2) в най-лошия случай.
** Пирамидално сортиране, сортиране чрез сливане: O(n log2n) и тази оценка не може да се подобри при
сортиране чрез сравняване.
** Сортиране
чрез трансформация [3.2]
** Сортиране чрез множество [3.2.1]
Дадено е множество M от числа в затворения интервал [a, b] и инективна функция за
нареждане f: M -> [a, b], т.е. ако x1
и x2 са
различни, то са различни и f
(x1) и f (x2).
Построяваме нулев масив S с индекси от a до b и с
едно минаване през множеството M
поставяме стойности 1 на S[f(x)] за
всяко x от M.
След това минаваме през масива S за да подредим елементите на M.
Пример:
M = { 5, 3, 2, 6 }, [1, 6],
1 2 3 4 5 6 1 2 3
4 5 6
S = 0,0,0,0,0,0; S = 0,1,1,0,1,1; M = { 2,
3, 5, 6 }
Сложност O(m+n), където n е
броят на елементите на M, а m = b - a + 1.
** Сортиране чрез броене [3.2.2]
Дефинираме масив cnt[], като cnt[i]
съдържа броя на срещанията на числото i.
** Побитово сортиране [3.2.3]
Идеята на побитовото сортиране се основава на двоичното вътрешно
представяне на числата в компютъра.
Нека е зададено множество от цели числа без знак, чиито елементи ще
сортираме.
Разделяме числата в два списъка в зависимост от стойността на
най-младшия им двоичен бит.
Четните числа попадат в първия списък, а нечетните - във втория.
Следва добавяне на втория списък към края на първия, при което
получаваме общ списък.
Повтаряме операцията с предпоследния бит, след това с
предпредпоследния и т.н.
Процесът приключва след извършване на операцията с най-старшия бит.
Сложност O(n).
** Метод на бройните системи [3.2.4]
Както побитовото, но по цифрите на числото в съответната бройна
система.
** Сортиране чрез пермутация [3.2.5]
Дадено е множество M
от n елементи. Означаваме
с S множеството {1, 2, 3,
..., n}. Функцията за нареждане
f: S -> S е сюрективна (инективна и върху), т.е. ако x1 и x2 са различни,
то f (x1) и f (x2) са различни за всяко y от S съществува x
от S такова, че y = f (x).
Разменяме m[1]
с m[m[1]] докато на 1-во място не дойде 1. После по същия начин с
втория елемент и т.н.
Пример:
позиции 1234567
4375612
5374612
6374512
1374562
1734562
1234567
Броят на размените не недвишава n, а броят на сравненията - 2n.
Бързо сортиране
А. Разделяне на дялове:
1. Избираме случаен елемент x от масива a
2. Преглеждаме масива отляво (от началото), докато достигнем
до елемент > x
3. Преглеждаме масива отдясно (от края), докато достигнем до
елемент < x
4. Разменяме местата на двата елемента
vector<int> a(n);
void partition(int x)
{
int i=1, j=n;
do
{
while (a[i] < x) i++;
while (a[j] > x) j--;
if (i<=j) { swap(a[i], a[j]); i++; j--; }
}
while (i<=j);
}
Б. Сортиране - след като масивът се раздели, двата му дяла се
подлагат на същата обработка и това продължава, докато се получат
дялове само с по един елемент.
//
qsort.cpp
void quicksort(int left, int right)
{
int i=left, j=right;
int x=a[(i + j)/2];
do
{
while (a[i] < x) i++;
while (a[j] > x) j--;
if (i<=j)
{ swap(a[i], a[j]); i++;
j--; }
}
while (i<=j);
if (left<j)
quicksort(left, j);
if (i<right) quicksort(i,
right);
}
Сортиране чрез сливане
mergesort.cpp
Сортиране в STL
- sort, qsort
- контейнери set, multiset
*
Домашно
Генериране на редица от n
случайни числа в интервала [0, k -
1].
for (int i = 0; i < n; i++) cout << rand()%k
<< " ";
* Състезание в събота
- формиране на отбори
- отборни стратегии