Сортиране чрез селекция
(пряка селекция).
Алгоритъмът се състои в следното: на всяка стъпка
намираме най-малкия елемент (от ненаредената част на масива) и го поставяме
на място (чрез размяна) в наредения масив.
Пример:
|
0 - елемент за размяна
0 - най-малък елемент 0* - елемент, който е вече е подреден |
Програмата ще реализираме със заглавни файлове и модули. Пряката селекция е в модула selsort (файлове selsort.h и selsort.cpp), функциите за генериране и отпечатване на вектор от цели чиса - в модула utilv (файлове utilv.h и utilv.cpp) и главна функция main във файла sels_test.cpp.
// selsort.h
#ifndef SELSORT_H
#define SELSORT_H
#include <vector>
using namespace std;
void selection_sort(vector<int> &);
#endif
// selsort.cpp
#include <vector>
#include "selsort.h"
using namespace std;
void swap(int &x, int &y)
{ int temp = x;
x = y;
y = temp;
}
int min_position(vector<int> &a,
int from, int to)
{ int min_pos = from;
for (int i = from + 1; i <=
to; i++)
if
(a[i] < a[min_pos]) min_pos = i;
return min_pos;
}
void selection_sort(vector<int> &a)
{ int next;
for (next = 0; next < a.size()-1;
next++)
{
int min_pos = min_position(a,
next, a.size()-1);
if (min_pos != next)
swap(a[min_pos], a[next]);
}
}
// end selsort.cpp
// utilv.h
#ifndef UTILV_H
#define UTILV_H
#include <vector>
using namespace std;
void print(vector<int>);
void generate(vector<int> &);
#endif
// utilv.cpp
#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>
#include "utilv.h"
using namespace std;
void print(vector<int> a)
{ int i;
for (i = 0; i < a.size(); i++)
cout << a[i] << " ";
cout << "\n";
}
void rand_seed()
{ int seed = static_cast<int>(time(0));
srand(seed);
}
int rand_int(int a, int b)
{ return a + rand() % (b - a + 1); }
void generate(vector<int> &a)
{ rand_seed();
int i;
for (i = 0; i < v.size(); i++)
v[i] = rand_int(1, 100);
}
// end utilv.cpp
// sels_test.cpp
#include <iostream>
#include <vector>
#include "selsort.h"
#include "utilv.h"
using namespace std;
int main()
{ cout << "Enter vector size: ";
int n;
cin >> n;
vector<int> v(n);
generate(v);
print(v);
selection_sort(v);
print(v);
return 0;
}
Enter vector size: 10
92 11 61 53 25 18 100 8 17 79 8 11 17 18 25 53 61 79 92 100 |
Измерване на времето за работа
на алгоритъма за сортиране чрез селекция.
Времето за работа на една програма можем да измерим
като използваме класа Time,
чийто конструктор без параметри конструира обект, съдържащ текущото време
на компютъра.
// sorttime.cpp
#include <iostream>
#include <vector>
#include "selsort.h"
#include "utilv.h"
using namespace std;
#include "ccc_time.cpp"
int main()
{ cout << "Enter vector size: ";
int n;
cin >> n;
vector<int> v(n);
generate(v);
Time before;
selection_sort(v);
Time after;
cout << "Elapsed time = "
<< after.seconds_from(before)
<<
" seconds\n";
return 0;
}
Enter vector size: 8000
Elapsed time = 54 seconds |
С няколко изпълнения на програмата получаваме следната таблица:
Vector size | Elapsed time |
1000 | 2 |
2000 | 4 |
4000 | 15 |
8000 | 54 |
Сортиране чрез сливане.
Ако двете половини на вектор a са
сортиране, то можем да слеем двете половинки в един сортиран вектор b.
Вектор a
|
Вектор b
|
void merge(vector<int> &a, int from,
int mid, int to)
/* ЦЕЛ: слива две съседни части на вектор
ПОЛУЧАВА: a - вектора, чиито
елемент си сливат
from, mid - начало и край на първата част
to - край на втората част
*/
{ int n = to - from + 1; /* размер на
частта, която се слива */
/* двете половинки се сливат във временен
вектор b */
vector<int> b(n);
int i1 = from;
/* следващият елемент от първата половина
*/
int i2 = mid + 1;
/* следващият елемент от втората половина
*/
int j = 0; /* следваща свободна
позиция в b */
/* докато нито i1 нито i2 са преминали
края, копираме по-малкия елемент в b */
while (i1 <= mid &&
i2 <= to)
{ if (a[i1] < a[i2]) { b[j]
= a[i1]; i1++; }
else
{ b[j] = a[i2]; i2++; }
j++;
}
/* само един от двата цикъла ще се изпълни
*/
/* копиране на оставащите елементи от
първата половина */
while (i1 <= mid) { b[j]
= a[i1]; i1++; j++; }
/* копиране на оставащите елементи от
втората половина */
while (i2 <= to)
{ b[j] = a[i2]; i2++; j++; }
/* копиране обратно от временния вектор
*/
for (j = 0; j < n; j++)
a[from + j] = b[j];
}
void merge_sort(vector<int> &a,
int from, int to)
{ if (from == to) return;
int mid = (from + to)/2;
/* сортиране на първата и втората половина
*/
merge_sort(a, from, mid);
merge_sort(a, mid+1, to);
merge(a, from, mid, to);
}
int main()
{ vector<int> v(20);
generate(v);
print(v);
merge_sort(v, 0, v.size()-1);
print(v);
return 0;
}
29 59 16 5 28 38 33 37 32 48 10 92 80
16 6 41 80 81 52 17
5 6 10 16 16 17 28 29 32 33 37 38 41 48 52 59 80 80 81 92 |
Анализ на алгоритъма за сортиране
чрез сливане.
На всяка стъпка от сливането правим сравнение между
един елемент от първата половина на a и един елемент от
втората половина на a и записваме един елемент в b,
т.е. правим 3 обръщения към елементи на масивите. За обратното копиране
на b в a имаме още 2 обръщения. Или общо
5n обръщения за вектор от n елемента. Нека T(n)
е функцията, която задава броя на обръщенията. Тогава поради двете рекурсивни
извиквания с два пъти по-малък вектор имаме: