1.冒泡排序
大一就学的必备技能,最简单的排序时i,j都从0-n进行判断swap,虽然也是O(n2)但这种排序低级到无法进入我们的十大排序中,所以要对其进行一下优化,就是i从0-n,但j可以从n-1到i,这样节省了不必要的操作。
冒泡就是说每个数与后面的比较,这样每次就会把未排序的最大的浮到后面。最坏(完全乱序,退化到刚才说的不入流方法)和平均时间复杂度为O(n2),最好就是O(n)(本来就有序,一趟完成),空间复杂度为O(1)。
void bubble(vector<int>& a) {
int n = a.size();
for (int i = 0; i < n-1; i++) //趟数
{
for (int j = n-2; j >=0; j--) //次数
{
if (a[j] > a[j + 1])
{
swap(a[j], a[j + 1]);
}
}
}
}
2.选择排序
就是每次从未排序数组中选择一个最小的放在已排序的最右边,思路很简单,就是每次都得遍历完全部数组才能找到最小的。最好最坏平均时间复杂度均为O(n2),空间复杂度为O(1)。
选择排序的元素的比较次数与初始序列无关,直接选择排序的时间复杂度与初始序列无关的。
void select(vector<int>&a)
{
int n = a.size();
for (int i = 0; i <n; i++) {
int index = i;
for (int j = i + 1; j <n; j++) {
if (a[j] < a[index])
index = j;
}
swap(a[i], a[index]);
}
}
3.插入排序
顾名思义,就是将当前遍历到的数插入到已排序数组里。它与选择排序相比,不需要每趟走完数组,只要找到合适的位置就可以结束该次循环了
时间复杂度为O(n2),最好O(n)。空间O(1).
直接插入排序
void insert(vector<int>& a) {
int n = a.size();
for (int i = 0; i < n; ++i) {
for (int j = 0; j < i; ++j) {
if (a[i] < a[j])
swap(a[i], a[j]);
}
}
}
折半插入:就是在插入时采用二分法来定位该插到哪,节省比较次数。
void binInsert(vector<int>& a) {
int low, high, m, temp, i, j;
for (i = 1; i < a.size(); i++) {
//折半查找应该插入的位置
low = 0;
high = i - 1;
while (low <= high) {
m = (low + high) / 2;
if (a[m] > a[i])
high = m - 1;
else
low = m + 1;
}
//统一移动元素,然后将这个元素插入到正确的位置
temp = a[i];
for (j = i; j > high + 1; j--) {
a[j] = a[j - 1];
}
a[high + 1] = temp;
}
}
4.希尔排序
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
我们来看下希尔排序的基本步骤,在此我们选择增量gap=length/2,缩小增量继续以gap = gap/2的方式,这种增量选择我们可以用一个序列来表示,{n/2,(n/2)/2…1},称为增量序列。希尔排序的增量序列的选择与证明是个数学难题,我们选择的这个增量序列是比较常用的,也是希尔建议的增量,称为希尔增量,但其实这个增量序列不是最优的。此处我们做示例使用希尔增量。
时间复杂度O(nlogn)或O(n根号n),空间复杂度O(1)。
void shellInsert(vector<int>& a,int d) {
int n = a.size();
for (int t=d;t<n;++t)
{
int i = t - d;
int tmp = a[t];
while (i >= 0 && a[i] > tmp) {
a[i + d] = a[i];
i -= d;
}
if (i != t - d)
a[i + d] = tmp;
}
}
void shellSort(vector<int>& a) {
int n = a.size();
int d = n / 2; //初始增量设为数组长度的一半
while (d >= 1)
{
shellInsert(a, d);
d = d / 2; //每次增量变为上次的二分之一
}
}
5.计数排序
计数排序是一种线性排序算法,不需要进行比较,时间复杂度为O(n)。(注意是计数排序不是基数排序,两者不同)
基本思想是:对于每个元素x,找出比x小的数的个数,从而确定x在排好序的数组中的位置。此算法需要辅助数组,是以空间换时间。
其实就是桶排序的优化,限制并计算好了额外空间的最佳大小。
void CountSort(vector<int> &arr, int maxVal) {
int len = arr.size();
if (len < 1)
return;
vector<int> count(maxVal+1, 0);
vector<int> tmp(arr);
for (auto x : arr)
count[x]++;
for (int i = 1; i <= maxVal; ++i)
count[i] += count[i - 1];
for (int i = len - 1; i >= 0; --i) {
arr[count[tmp[i]] - 1] = tmp[i];
count[tmp[i]]--; //注意这里要减1
}
}