归并排序是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
归并排序的思想类似于二叉树的后序遍历操作,归并排序就是先把左半边数组排好序,再把右半边数组排好序,然后把两半数组合并,我们可以把归并排序的过程想象成一棵树:
在每个节点的后序位置(左右子节点已经被排好序)的时候执行 merge 函数,合并两个子节点上的子数组:
归并排序体现了 “分而治之” 的算法思想,具体为:
如下图所示,为数组 [7,3,5,1,6,4,8,2] 的归并排序过程:
首先计算数组的中点mid,递归划分左子数组_MergeSort(a, left, mid)和递归划分右子数组_MergeSort(a, mid+1, right),当left >= right时,停止划分,开始归并子数组
if (left >= right)
return;
int mid = (left + right) >> 1;
_MergeSort(a, left, mid, tmp);
_MergeSort(a, mid + 1, right, tmp);
设置双指针begin和end分别指向左/右子数组的首元素和尾元素,每次较小的元素放进tmp数组中,归并完毕,将tmp数组中的数组拷贝回原数组
int begin1 = left, end1 = mid;
int begin2 = mid + 1, end2 = right;
int i = left;
while (begin1 <= end1 && begin2 <= end2)
{
if (a[begin1] < a[begin2])
{
tmp[i++] = a[begin1++];
}
else
{
tmp[i++] = a[begin2++];
}
}
while (begin1 <= end1)
{
tmp[i++] = a[begin1++];
}
while (begin2 <= end2)
{
tmp[i++] = a[begin2++];
}
for (int j = left; j <= right; ++j)
{
a[j] = tmp[j];
}
完整归并排序参考代码:
void _MergeSort(int* a, int left, int right, int* tmp)
{
if (left >= right)
return;
// 划分区间
int mid = (left + right) >> 1;
_MergeSort(a, left, mid, tmp);
_MergeSort(a, mid + 1, right, tmp);
// 合并子数组
int begin1 = left, end1 = mid;
int begin2 = mid + 1, end2 = right;
int i = left;
while (begin1 <= end1 && begin2 <= end2)
{
if (a[begin1] < a[begin2])
{
tmp[i++] = a[begin1++];
}
else
{
tmp[i++] = a[begin2++];
}
}
while (begin1 <= end1)
{
tmp[i++] = a[begin1++];
}
while (begin2 <= end2)
{
tmp[i++] = a[begin2++];
}
for (int j = left; j <= right; ++j)
{
a[j] = tmp[j];
}
}
// 归并排序
void MergeSort(int* a, int n)
{
int* tmp = (int*)malloc(sizeof(int) * n);
if (tmp == NULL)
{
printf("malloc failed\n");
exit(-1);
}
_MergeSort(a, 0, n - 1, tmp);
free(tmp);
tmp = NULL;
}
归并排序的特性总结:
计数排序是一个非基于比较的排序算法,该算法于1954年由 Harold H. Seward 提出。它的优势在于在对一定范围内的整数排序时,它的复杂度为Ο(n+range)(其中range是整数的范围),快于任何比较排序算法。 当然这是一种牺牲空间换取时间的做法,而且当O(range)>O(n*log(n))的时候其效率反而不如基于比较的排序(基于比较的排序的时间复杂度在理论上的下限是O(n*log(n)), 如归并排序,堆排序)
动图演示:
基本步骤:
参考代码:
// 计数排序
void CountSort(int* a, int n)
{
int min = a[0], max = a[0];
for (int i = 1; i < n; ++i)
{
if (a[i] > max)
{
max = a[i];
}
if (a[i] < min)
{
min = a[i];
}
}
int range = max - min + 1;
int* count = (int*)malloc(sizeof(int) * range);
memset(count, 0, sizeof(int) * range);
if (count == NULL)
{
printf("malloc failed\n");
exit(-1);
}
for (int i = 0; i < n; ++i)
{
count[a[i] - min]++;
}
int j = 0;
for (int i = 0; i < n; ++i)
{
while (count[i]--)
{
a[j++] = i + min;
}
}
}
计数排序的特性总结: