1. 插入排序
直接插入
将待排序元素插入到前面已经有序的数组中,直至将待排序元素全部插完(过程如下图)
具体步骤:
(1)比较待插入元素(key)和有序序列的最后一个元素(a[end]),如果 key>=a[end],直接插入,反之交换key和a[end]
(2)如果key< a[end],继续在有序区间里寻找小于key的元素(tmp),将key插入到tmp的后面希尔排序
是直接插入排序的优化;当待排序元素较多时,直接插入的效率反而会变得很低,越接近有序的序列使用直接插入排序时,效率越高,故在进行插入排序之前进行预排序,使其待排序序列接近有序(如下图)
二者算法分析
稳定性
- 直接插入排序是稳定算法,排序码的相对位置不会改变
- 希尔排序是不稳定算法,在进行预排序时,排序码的相对位置可能发生改变
时间复杂度
- 直接插入排序
- 最优情况:待排序序列越接近有序,算法效率越高,时间复杂度为O(N)
- 最差情况:当待排序序列很大,并且不是很接近有序,算法效率大大降低,时间复杂度为O( n2 n 2 )
- 希尔排序
希尔排序是直接插入排序的优化,但当待排序数列很大时,预排序过程效率会降低许多,时间复杂度为O( n2 n 2 )
空间复杂度
空间复杂度都为O(1),在进行排序时,会创建临时常数个临时变量用于控制下标
代码:
/*****************直接插入排序************/
void InsertSort(DataType* a, size_t n)
{
assert(a);
int end;
for (size_t i = 0; i < n - 1; ++i)
{
end = i;
while (end >= 0 && a[end]>a[end + 1])
{
//待插入序列大于有序序列的最后一个
Swap(&a[end], &a[end + 1]);
--end;
}
}
}
/**********希尔排序******************/
void ShellSort(DataType* a, size_t n)
{
assert(a);
//先进行预排序
int gap = n / 3 + 1;//进行预排序时的跨度
while (gap > 1)
{
for (size_t i = 0; i < n - gap; ++i)
{
size_t end = i;
while (end >= 0 && a[end] >= a[end+gap])
{
Swap(&a[end], &a[end + gap]);
end -= gap;
}
}
gap = gap / 3 + 1;
}
InsertSort(a, n);
}
2.选择排序
- 选择排序
每遍历一次待排序序列,选出最大和最小的数,分别放在左边和右边的相应位置上,直到全部选出为止(如下图)
- 堆排序
将待排序序列建堆,每次将堆顶数据与堆尾数据进行交换,在进行堆的调整(部分过程图)
二者算法分析
稳定性
- 直接选择排序是不稳定算法,排序码的相对位置会发生改变
- 堆排序是不稳定算法,在进行建堆和调整堆时,排序码的相对位置发生改变
时间复杂度
- 直接选择排序
- 时间复杂度为O(N*N)
- 移动次数最好情况为0次
- 堆排序
- 建堆的时间复杂度为O(N*logN)
- 建堆后,每次排序时间复杂度为O(logN)
综上,堆排序的时间复杂度为O(N*logN)
空间复杂度
- 空间复杂度都为O(1),在进行排序时,会创建常数个临时变量用于控置下标
- 需要占用一个临时空间,在交换数值时使用。
代码
/*******************选择排序*************/
void SelectSort(DataType* a, size_t n)
{
assert(a);
size_t left = 0;
size_t right = n - 1;
while (left < right)
{
//排序结束条件,两边错过
size_t min = left;
size_t max = left;
for (size_t i = left; i <=right ; ++i)
{
//遍历数组,选出最小的数和最大的数
if (a[min] > a[i])
{
//放到最左边
min = i;
}
if (a[max] < a[i])
{
//放到最右边
max = i;
}
}
//进行交换
Swap(&a[left], &a[min]);
//当最大的数在最左边,需将max的位置置为最小值
if (max == left)
{
max = min;
}
Swap(&a[right], &a[max]);
left++, right--;
}
}
/*****************堆排序****************/
void HeapSort(DataType* a1, size_t n)
{
assert(a);
//建堆,升序建大堆,降序建小堆
int i = (n - 2) >> 1;
for (; i >= 0; --i)
{
AdjustDown(a, n, i);
}
for (int j = n-1;j>0; --j)
{
Swap(&a[j], &a[0]);
AdjustDown(a, j, 0);
}
}
//向下调整
void AdjustDown(DataType* a1, size_t n, size_t parent)
{
size_t child = parent * 2 + 1;
while (child < n)
{
//选出左右孩子当中较大的
if ((child + 1) < n && a[child] < a[child + 1])
{
++child;
}
if (a[parent] < a[child])
{
Swap(&a[parent], &a[child]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}