常见的排序算法——选择排序
程序员文章站
2022-06-06 20:46:11
...
选择排序
基本思想
每一趟在后面 n - i 个待排序的数据元素集合中选择出关键码最小的数据元素,作为有序元素序列的第i个元素。待 到第 n - 2 趟做完,待排序元素集合中只剩下一个元素,排序结束。
直接选择排序
- 在元素集合array[i]—-array[n-1]中选择关键码最大(小)的数据元素
- 若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换。
- 在剩余的array[i]—-array[n-2](array[i+1]—-array[n-1])的集合中,重复上述步骤,直到集合剩余1一个元素
代码如下
void Swop(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
void Select(int array[], int size)
{
int end = size - 1;
int begin = 0;
int i = 0;
//这里i的终止条件必须是size
//如果是end,就会少排几次
for ( i = begin; i <= size; ++i)
{
int Maxpos = begin;
for (int j = begin; j <= end; ++j)
{
if (array[Maxpos] < array[j])
Maxpos = j;
}
if (Maxpos != end)
Swop(&array[Maxpos], &array[end]);
--end;
}
}
我们可以对其在进行优化,一次遍历找到一个最大的元素,一个最小的元素,这样能节省时间。
void Select_OP(int array[], int size)
{
int end = size - 1;
int begin = 0;
//因为end为size-1,闭区间,所以这里的i要小于等于end
//终止条件不是size,是因为
for (int i = begin; i <= end; ++i)
{
int Maxpos = begin;
int Minpos = begin;
for (int j = begin + 1; j <= end; ++j)
{
if (array[Maxpos] < array[j])
Maxpos = j;
if (array[Minpos] > array[j])
Minpos = j;
}
if (Maxpos != end)
Swop(&array[Maxpos], &array[end]);
//如果最小值在末尾,最小值就会被换走,因此要更新Minpos的值
if (Minpos == end)
Minpos = Maxpos;
if (Minpos != begin)
Swop(&array[Minpos], &array[begin]);
--end;
++begin;
}
}
总结
- 直接选择排序的时间复杂度为O(n^2)
- 空间复杂度为O(1)
- 它是一种不稳定的排序算法
- 缺点是比较次数太多
堆排序
创建堆:升序—>大堆,降序—>小堆
具体步骤:
- 建堆
- 把堆顶array[0]元素和当前最堆的最后一个元素交换
- 堆元素个数减1
- 由于第1步后根节点不再满足最堆定义,向下调整根结点
代码如下:
void AdjustDown(int array[], int size, int parent)
{
int child = (parent << 1) + 1;
//这里就是所说的循环条件
while (child <= size)
{
//找到两个孩子中较大的一个
if (child + 1 <= size && array[child + 1] > array[child])
{
child = child + 1;
}
//比较parent和孩子的大小
if (array[parent] < array[child])
{
Swop(&array[parent], &array[child]);
parent = child;
child = (parent << 1) + 1;
}
else
{
break;
}
}
}
//升序-->将大堆
void HeapSort(int array[], int size)
{
int root = (size - 2) >> 1;
int end = size - 1;
//建堆
while (root >= 0)
{
AdjustDown(array, size, root);
root--;
}
//排序
while (end >= 0)
{
//交换根节点和最后一个元素
Swop(&array[end], &array[0]);
//这里的end--和向下调整的顺序,取决于向下调整里的循环条件
//如果是闭区间,则end需要在前边,如果是开区间,则end在后边
//最后一个元素已经排好,因此下次调整堆,不应包括此节点
end--;
//不满足大堆性质,调整
AdjustDown(array, end, 0);
}
}
总结
- 把一棵完全二叉树调整为堆,以及每次将堆顶元素交换后
进行调整的时间复杂度均为O( lgn),所以堆排序的时间复杂度为:O(nlgn ) - 空间复杂度为O(1)
- 堆排序是一种不稳定的排序算法
上一篇: 经典排序--》选择排序--》详解