// Sort.h
#pragma once
#include
#include
#include
#include
#include "Stack.h"
// 插入排序
void InsertSort(int* a, int n);
// 希尔排序
void ShellSort(int* a, int n);
// 选择排序
void SelectSort(int* a, int n);
// 堆排序
void AdjustDwon(int* a, int n, int root);
void HeapSort(int* a, int n);
// 冒泡排序
void BubbleSort(int* a, int n);
升序排序
具体是如何插入的:
// 插入排序
void InsertSort(int* a, int n)
{
assert(a);
for (int i = 1; i < n; i++)
{
int tmp = a[i];
int end = i - 1;
while (end >= 0)
{
if (a[end] <= tmp)
{
break;
}
a[end + 1] = a[end--];
}
a[end + 1] = tmp;
}
}
array[0]
挪动数据并插入的次数为:0;array[1]
挪动数据并插入的次数为:1;array[2]
挪动数据并插入的次数为:2;array[3]
挪动数据并插入的次数为:3;array[n]
挪动数据并插入的次数为:n;∴ 时间复杂度O(N)=N²
插入排序的适应性很强,对于有序、局部有序的情况,都能效率提升
为了优化直接插入,我们选择进行预排序,让较大的位于前列的数先排到较后面的位置,以此减少挪动数据的次数
预排序:分组,每组进行插入排序
while(gap)
{
int gap = n / 2;
gap /= 2;
}
//当gap==1时就是直接插入排序
//or
while(gap)
{
int gap = n / 3;
gap = (gap/3 + 1);
}
// 希尔排序
void ShellSort(int* a, int n)
{
assert(a);
int gap = n / 2;
while (gap)
{
for (int i = 1; i < n; i++)
{
int tmp = a[i];
int end = i - gap;
while (end >= 0)
{
if (a[end] <= tmp)
{
break;
}
a[end + gap] = a[end];
end -= gap;
}
a[end + gap] = tmp;
}
gap /= 2;
}
}
希尔排序的时间复杂度算起来很复杂,预排序之后无法确定数列的顺序情况。
这里直接给结果:O(N)=N^1.3
遍历数组(以排升序为例)
找到最一小的数与数组第一个位置的数交换;
找到第二小的数与数组第二个位置的数交换;
……(依次类推)
优化:两头缩进
从数组的头和尾开始,在中间同时找最大和最小的数,找到分别与“两头的数”交换,依次类推,“两头缩进”
//优化后:
void Swap(int* x, int* y)
{
assert(x && y);
int tmp = *x;
*x = *y;
*y = tmp;
}
// 选择排序
void SelectSort(int* a, int n)
{
assert(a);
//i < (n - 1) / 2 即 begin < end 即 i
for (int i = 0; i < (n - 1) / 2; i++)
{
int begin = i, end = n - 1 - i;
int mini = begin, maxi = end;
//[begin+1,end-1]在这个区间内找到最大和最小的数
for (int j = begin + 1; j <= end - 1; j++)
{
if (a[j] < a[mini])
mini = j;
if (a[j] > a[maxi])
maxi = j;
}
//min = a[mini], max = a[maxi] 找到之后交换
Swap(&a[begin], &a[mini]);
Swap(&a[end], &a[maxi]);
}
}
未优化的算法:
从头找到尾
找到最一小的数与数组第一个位置的数交换,查找的次数为:n - 1;
找到第二小的数与数组第二个位置的数交换,查找的次数为:n - 2;
…………
最后一次查找次数为:1( n -(n-1))
Σ = 1+2+3+……+(n-1)= n*(n-1)/2
∴ 时间复杂度为O(N²)
详细分析过程见→二叉树-堆| [堆的实现【建堆算法图解+分析】]
排升序:建大堆。把数组按大堆的方式排列,此时堆顶就是最大的数,让堆顶与数组最后一个数交换,最大的数就被放在了数组的尾部。对于前面的部分,调整使其依旧为大堆,此时堆顶的数就是第二大的数……重复这个操作向下调整建堆,不断取堆顶的数
排降序:建小堆。思路同上。
// 堆排序
void AdjustDwon(int* a, int n, int root)
{
assert(a);
int parent = root;
int child = parent * 2 + 1;
while (child < n)
{
if ((child + 1) < n && a[child + 1] > a[child])
++child;
if (a[parent] < a[child])
Swap(&a[parent], &a[child]);
else
break;
parent = child;
child = parent * 2 + 1;
}
}
void HeapSort(int* a, int n)
{
assert(a);
//建大堆
for (int parent = (n - 1 - 1) / 2; parent >= 0; parent--)
{
AdjustDwon(a, n, parent);
}
//取堆顶的数据并向下调整
int end = n - 1;
while (end)
{
Swap(&a[0], &a[end]);
AdjustDwon(a, end, 0);
end--;
}
}
向下调整建堆的时间复杂度:O(N)
取堆顶数据然后向下调整时间复杂度:O(logN) → 重复 N次 → O(N*logN)
∴ 时间复杂度 为 O(N*logN)
前后比较交换
// 冒泡排序
void BubbleSort(int* a, int n)
{
assert(a);
for (int j = 0; j < n; j++)
{
for (int i = 0; i < n - j - 1; i++)
{
if (a[i] > a[i + 1])
Swap(&a[i], &a[i + 1]);
}
}
}
第一趟冒泡排序:n-1次
第二趟冒泡排序:n-2次
……
第n趟冒泡排序:n-n次
Σ = 1 + 2 + 3 + …… + n-1 = n(n-1)/2
∴时间复杂度为O(N)=N²