文章目录
- 直接插入排序
- 希尔排序
直接插入排序
把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列。
如果tmp比end的数大或者相等,就继续放在end后面。
如果比end的数小,就让end的数往后挪一位,将3覆盖掉了。
下面看更复杂的情况,当tmp = 1时,end要减到-1结束。
void InsertSort(int* a, int n)
{
for (int i = 0; i < n-1; i++)
{
int end = i;
int tmp = a[end + 1];
while (end >= 0)
{
if (a[end] > tmp)
{
a[end + 1] = a[end];
--end;
}
else
{
break;
}
}
a[end + 1] = tmp;
}
}
插入排序的时间复杂度最坏为O(N^2^)
,最好为O(N)
下面我们测试一下堆排序和插入排序的性能:
堆排序:
void Swap(int* p1, int* p2)
{
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
void AdjustDown(int* a, int n, int parent)
{
int minChild = parent * 2 + 1;
while (minChild < n)
{
// 找出小的那个孩子
if (minChild + 1 < n && a[minChild + 1] > a[minChild])
{
minChild++;
}
if (a[minChild] > a[parent])
{
Swap(&a[minChild], &a[parent]);
parent = minChild;
minChild = parent * 2 + 1;
}
else
{
break;
}
}
}
// O(N*logN)
void HeapSort(int* a, int n)
{
// 大思路:选择排序,依次选数,从后往前排
// 升序 -- 大堆
// 降序 -- 小堆
// 建堆 -- 向下调整建堆 - O(N)
for (int i = (n - 1 - 1) / 2; i >= 0; --i)
{
AdjustDown(a, n, i);
}
// 选数
int i = 1;
while (i < n)
{
Swap(&a[0], &a[n - i]);
AdjustDown(a, n - i, 0);
++i;
}
}
测试用例:
void TestOP()
{
srand(time(0));
const int N = 1000000;
int* a1 = (int*)malloc(sizeof(int) * N);
int* a4 = (int*)malloc(sizeof(int) * N);
;
for (int i = 0; i < N; ++i)
{
a1[i] = rand();
a4[i] = a1[i];
}
int begin1 = clock();
InsertSort(a1, N);
int end1 = clock();
int begin4 = clock();
HeapSort(a4, N);
int end4 = clock();
printf("InsertSort:%d\n", end1 - begin1);
printf("HeapSort:%d\n", end4 - begin4);
free(a1);
free(a4);
}
int main()
{
TestOP();
return 0;
}
排完取时间:单位是毫秒
希尔排序
希尔排序又称缩小增量法。希尔排序法的基本思想是:先进行预排序,再进行最后的一个插入排序。
1.预排序目的:接近排序。
2.直接插入排序。
预排序:间隔为gap的数据分为一组进行插入排序。
针对升序,gap越大,大的数据可以越快跳到后面,小的数据可以更快的跳到前面。gap越小,跳的越慢,越接近有序。
排完之后的数据也是没有序的,但是相比于原数据,小的数据偏前面,大的数据偏后面,小的数据往前面挪相对快一点,大的也是。一次跳跃挪gap步。
gap > 1 预排序
gap == 1 直接插入排序
希尔排序:
void PrintArray(int* a, int n)
{
for (int i = 0; i < n; i++)
{
printf("%d ", a[i]);
}
printf("\n");
}
void ShellSort(int* a, int n)
{
//gap > 1 预排序
//gap == 1 直接插入排序
int gap = n;
while (gap > 1)
{
gap = (gap / 3) + 1;
for (int i = 0; i < n - gap; i++)
{
//[0,end] 插入 end+gap [0,end+gap]有序 --间隔为gap的数据
int end = i;
int tmp = a[end + gap];
while (end >= 0)
{
if (a[end] > tmp)
{
a[end + gap] = a[end];
end -= gap;
}
else
{
break;
}
}
a[end + gap] = tmp;
}
}
}
void TestShellSort()
{
int a[] = {9,8,7,6,5,4,3,2,1,0};
ShellSort(a, sizeof(a) / sizeof(int));
PrintArray(a, sizeof(a) / sizeof(int));
}
int main()
{
TestShellSort();
return 0;
}
我们测试看看希尔排序的效率:
void TestOP()
{
srand(time(0));
const int N = 10000;
int* a1 = (int*)malloc(sizeof(int) * N);
int* a2 = (int*)malloc(sizeof(int) * N);
for (int i = 0; i < N; ++i)
{
a1[i] = rand();
a2[i] = a1[i];
}
int begin1 = clock();
InsertSort(a1, N);
int end1 = clock();
int begin2 = clock();
ShellSort(a2, N);
int end2 = clock();
printf("InsertSort:%d\n", end1 - begin1);
printf("ShellSort:%d\n", end2 - begin2);
free(a1);
free(a2);
free(a3);
free(a4);
free(a5);
free(a6);
}
int main()
{
TestOP();
return 0;
}
10000个数据:
100000个数据:
堆排序、插入排序、希尔排序相比较,堆排序和希尔排序属于同一量级的。
总结:
- 希尔排序是对直接插入排序的优化。
- 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。
- 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算。
我们可以看看一些书中的解释;