常见的排序算法
一:插入排序
直接插入排序:[0,end]有序,将end+1 插入到区间[0,end],保持有序。
举例如图所示:
数组为 [ 3,4,2,1,6,0,9,5 ]
实现代码:
// 插入排序
void InsertSort(int* a, int n)
{
for (int i = 0; i < n - 1;i++)
{
//将end+1 往有序区间[0,end]中插入,使其保持有序
int end = i;
int insert = a[end + 1];
while (end >= 0)
{
if (insert < a[end])
{
a[end + 1] = a[end];
end--;
}
else
{
break;
}
}
//将insert放在[end+1]位置
a[end + 1] = insert;
}
}
希尔排序:先选定一个整数gap,把待排序文件中所有记录分成组,所有距离相同的为一组,并对组进行排序。然后,取,重复上述分组和排序的工作。当到达gap==1时,所有记录在统一组内排好序。
数组为:[ 9,8,7,6,5,4,3,2,1,0 ]
实现代码:
// 希尔排序
void ShellSort(int* a, int n)
{
//1. gap>1是预排序,让数组接近有序
//2. gap==1,就相当于直接插入排序,保证有序
int gap = n;
//多组并排
while (gap > 1)
{
gap = gap / 3 + 1; //+1保证了最后一次gap一定是1
//gap==1,最后一次相当于直接插入排序
for (int i = 0; i < n - gap; ++i)
{
int end = i;
int insert = a[end + gap];
while (end >= 0)
{
if (insert < a[end])
{
a[end + gap] = a[end];
end -= gap;
}
else
{
break;
}
}
a[end + gap] = insert;
}
}
}
二:选择排序
直接选择排序:在元素集合array[i]–array[j]中选最小和最大的数据元素,若它不是这组元素中的第i个和第j个,则将它们和第i,第j位置元素交换,在剩余的array[i+1]–array[j-1]集合中,重复上述步骤,直到集合剩余1个元素
实现代码:
// 选择排序
void SelectSort(int* a, int n)
{
int begin = 0;
int end = n - 1;
while (begin < end)
{
int mini;
int maxi;
mini = maxi = begin;
for (int i = begin; i <= end; i++)
{
if (a[i] > a[maxi])
maxi = i;
if(a[i] < a[mini])
mini = i;
}
Swap(&a[begin], &a[mini]);
//如果begin和maxi位置重叠,需要对maxi进行修正
if (begin == maxi)
{
maxi = mini;
}
Swap(&a[end], &a[maxi]);
++begin;
--end;
}
}
堆排序:需要注意的是排升序要建大堆,排降序建小堆。
实现代码:
// 堆排序
//向下调整算法
void AdjustDown(int* a, int n, int root)
{
int parent = root;
int child = parent * 2 + 1;
while (child < n)
{
//找出左右孩子中大的数据给child
if (child + 1< n && a[child + 1] > a[child])
++child;
//如果child位置数据大于parent位置数据,进行交换
if (a[child] > a[parent])
{
Swap(&a[child], &a[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
//时间复杂度:O(N*logN)
void HeapSort(int* a, int n)
{
//建大堆
for (int i = (n - 1 - 1) / 2; i >= 0; --i)
{
AdjustDown(a, n , i);
}
int end = n - 1;
//用建大堆算法排升序
for (end = n - 1; end >= 0; --end)
{
//将堆顶数据与a[end]位置数据进行交换
Swap(&a[end], &a[0]);
//将前 end 个数据继续用向下调整算法进行建大堆
AdjustDown(a, end, 0);
}
}
为什么不能排升序建小堆?
如果用建小堆,选出最小的数据,再次建堆选出次小的,整个排出来的时间复杂度为O(N^2)。
通过建大堆,每次从堆顶选出最大数据,将其与堆内最后一个值进行交换,然后不将最后一个值看作堆里,堆的大小减1,再用向下调整算法调整为大堆,依次选出次小的,换堆内最后一个位置,再继续直到堆内数据为1个。