学习了一些常见的排序算法,我在这里对它们进行一个梳理,我们以升序为例子。
常见排序算法主要有:直接插入排序,希尔排序,选择排序,堆排序,冒泡排序,快速排序,归并排序,计数排序。
稳定性:算法在实现的过程中是否会改变相同元素的相对位置,不改变则稳定。
1.直接插入排序
- 主要代码
void InsertSort(int* arr, int n)
{
for (int i = 0; i < n-1; i++)
{
int end = i;
int tmp = arr[end + 1];//超过N就越界了,所以是N-1。
while (end >= 0 && arr[end] > tmp)
{
arr[end + 1] = arr[end];
--end;
}
arr[end + 1] = tmp;
}
}
- 空间复杂度:O(1),没有开辟新空间。
- 时间复杂度
最好情况:与目标顺序相同,仅仅需要O(n)
最坏情况:与目标顺序逆序,需要O(n2)
平均时间复杂度:O(n2) - 稳定性:稳定,数据相同时,通过arr[end+1]= tmp,数据的相对顺序不变.
2.希尔排序
- 主要代码
void ShellSort(int* arr, int n)
{
int gap = n ;
while (gap>1)
{
gap = (gap / 3) + 1;
for (int i = 0; i < n - gap; i++)
{
int end = i;
int tmp = arr[end + gap];
while (end >= 0 && arr[end] > tmp)
{
arr[end + gap] = arr[end];
end = end - gap;
}
arr[end + gap] = tmp;
}
}
}
- 空间复杂度:O(1),没有开辟新空间。
- 时间复杂度
平均时间复杂度: O(N1.3—N2) - 稳定性:不稳定,调整时相同数据的相对位置会改变。
3.直接选择
- 主要代码
void SelectSort(int* arr, int n)
{
int left = 0;
int right = n - 1;
while (left < right)
{
int max = left;
int min = left;
for (int i = left; i <= right; i++)
{
if (arr[i] > arr[max])
max = i;
if (arr[i] < arr[min])
min = i;
}
Swap(&arr[min], &arr[left]);
if (left == max)
{
Swap(&arr[min], &arr[max]);
}
Swap(&arr[max], &arr[right]);
++left;
--right;
}
}
- 空间复杂度:O(1),没有开辟新空间。
- 时间复杂度
最好情况,和最坏情况都需要O(n2)
平均时间复杂度: O(N2) - 稳定性:不稳定,相同数据相对位置会改变,在前面的数据会被交换都末尾。(一次选出两个数,不稳定,一次选一个数稳定,和实现的方法有关系)
4.堆排序
- 主要代码
void AdjustDown(int* arr, int root, int n)
{
int parent = root;
int child = 2 * parent + 1;
while (child<n)
{
if (child + 1 < n && arr[child] < arr[child + 1])
{
++child;
}
if (arr[parent] < arr[child])
{
Swap(&arr[parent], &arr[child]);
parent = child;
child = 2 * parent + 1;
}
else
{
break;
}
}
}
void HeapSort(int* arr, int n)
{
for (int i = (n-2)/2; i>=0; --i)
{
AdjustDown(arr, i, n);
}
int end = n-1;
while (end)
{
Swap(&arr[0], &arr[end]);
--end;
AdjustDown(arr, 0, end);
}
}
- 空间复杂度:O(1),没有开辟新空间。
- 时间复杂度
最好情况:与目标顺序相同,需要O(nlogn),
最坏情况:与目标顺序逆序,需要O(nlogn)
平均时间复杂度: O(nlogn) - 稳定性:不稳定,同层相同值的数据,会根据parent的情况,随机到堆顶,所以相对位置是会改变的。
5.冒泡排序
- 主要代码
void BubbleSort(int* arr, int len)
{
for (int i = 0; i <= len - 1; i++)
{
int flag = 0;
for (int j = 1; j <= len - i - 1; j++)
{
if (arr[j - 1] > arr[j])
{
flag = 1;
int tmp = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = tmp;
}
}
if (flag == 0)
{
break;
}
}
}
- 空间复杂度:O(1),没有开辟新空间。
- 时间复杂度
最好情况:与目标顺序相同,需要O(n),
最坏情况:与目标顺序逆序,需要O(n2)
平均时间复杂度: O(n2) - 稳定性:稳定
6.快速排序
- 主要代码
int _QuickSort(int* arr, int begin, int end)
{
int key = arr[end];
int index = end;
while (begin < end)
{
while (begin < end)
{
if (arr[begin] > key)
{
break;
}
++begin;
}
while (begin < end)
{
if (arr[end] <key)
{
break;
}
--end;
}
Swap(&arr[begin], &arr[end]);
}
Swap(&arr[end], &arr[index]);
return end;
}
void QuickSort(int* arr,int left, int right )
{
int begin = left;
int end = right;
if (begin >= end)
{
return;
}
int index = _QuickSort(arr, begin, end-1);
QuickSort(arr, 0, index - 1);
QuickSort(arr, index + 1, end);
}
- 空间复杂度:O(logn–n)
- 时间复杂度
最好情况:Key每次都能把数组分成相对均匀的两段,需要O(nlogn),
最坏情况:与目标顺序逆序,退化成冒泡排序,需要O(n2)
平均时间复杂度: O(nlogn) - 稳定性:不稳定,交换时无法保证相同数据的相对顺序不变。
7.归并排序
-
- 主要代码
void Merge(int* arr, int begin, int end,int* tmp)
{
if (begin >= end)
{
return;
}
int mid = (begin + end) >> 1;
Merge(arr, begin, mid,tmp);
Merge(arr, mid + 1, end,tmp);
int begin1 = begin, end1 = mid;
int begin2 = mid + 1,end2 = end;
int index = begin;
while (begin1 <=end1 && begin2 <= end2)
{
if (arr[begin1] > arr[begin2])
{
tmp[index++] = arr[begin2++];
}
else
{
tmp[index++] = arr[begin1++];
}
}
//第二段结束了,第一段还有
if (begin1 <= end1)
{
while (begin1<=end1)
{
tmp[index++] = arr[begin1++];
}
}
//第一段结束了,第二段还有
else
{
while (begin2 <= end2)
{
tmp[index++] = arr[begin2++];
}
}
//每次都要拷贝回原数组,并且长度为为end-begin+1个
memcpy(arr + begin, tmp + begin, sizeof(int)*(end - begin+1));
}
void MergeSort(int* arr,int n)
{
int *tmp = (int *)malloc(sizeof(int)*n);
Merge(arr, 0, n-1,tmp);
free(tmp);
}
- 空间复杂度:O(n),
- 时间复杂度
最好情况:逆序O(nlogn)
最坏情况:逆序O(nlogn)
平均时间复杂度: O(nlogn) - 稳定性:稳定
8.基数排序
void CountSort(int *arr,int n)
{
int max , min = arr[0];
max = min;
for (int i = 0; i < n; i++)
{
if (arr[i] > max)
{
max = arr[i];
}
if (arr[i]<=min)
{
min = arr[i];
}
}
int range = max - min+1;
int* a = (int*)malloc(sizeof(int)*range);
for (int i = 0; i < range; i++)
{
a[i] = 0;
}
int count = 0;
for (int i = 0; i < n; i++)
{
a[arr[i] - min ]++;
}
for (int i = 0; i < range; i++)
{
while (a[i] > 0)
{
arr[count] = i + min;
count++;
a[i]--;
}
}
free(a);
}
- 空间复杂度 O(范围)
- 时间复杂度 O(max(N,范围))
- 稳定性:稳定