欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

常见的排序算法——选择排序

程序员文章站 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)
  • 堆排序是一种不稳定的排序算法
相关标签: 选择排序