八大排序算法之选择排序

前面已经介绍了四种排序算法—-直接插入排序、希尔排序、冒泡排序和快速排序,今天再介绍两种排序算法:直接选择排序和堆排序。
这里还是以升序为例进行说明。

一、直接选择排序
1.基本思想
直接选择排序也是比较简单,容易理解的排序算法。对于一个有N个数的序列,每次遍历过程中找到序列中值最大或者最小的元素的下标并记录下来。并将其和最左边或者最右边的元素进行交换,这样的话就可以保证每次遍历都能排序好一个元素。依此类推,排序所有的元素。
2.排序过程
直接选择排序中的选择体现在每次选择一个值最大或者值最小的元素进行交换。下面的图是一次排序过程:

这里写图片描述

3.代码实现
#include <stdio.h>
#include <stdlib.h>

void PrintArray(int *pArray, int iLen);
void Swap(int *iLhs, int *iRhs);
void SelectSort(int *pArray, int iLen);

int main(void)
{
    int iArray[] = {9, 7, 6, 5, 3 };
    int iLen = sizeof(iArray) / sizeof(*iArray);    //计算数组元素个数
    PrintArray(iArray, iLen);
    SelectSort(iArray, iLen);
    PrintArray(iArray, iLen);
    return 0;
}

void PrintArray(int *pArray, int iLen)
{
    int iIndex;
    for (iIndex = 0; iIndex < iLen; ++iIndex)
    {
        printf("%5d", pArray[iIndex]);
    }
    printf("\n");
}

void Swap(int *iLhs, int *iRhs)
{
    int iTemp = *iLhs;
    *iLhs = *iRhs;
    *iRhs = iTemp;
}

void SelectSort(int *pArray, int iLen)
{
    int iCount;
    int iIndex;
    for (iCount = 0; iCount < iLen - 1; ++iCount)  //N个数需要N-1趟
    {
        int iMinIndex = iCount;
        for (iIndex = iCount + 1; iIndex < iLen; ++iIndex)
        {
            if (pArray[iMinIndex] > pArray[iIndex])     //每次将比较的两个元素中值较大的元素下标记录下来,一趟完了以后iMaxIndex存的是所有数据中值最大的元素的下标
            {
                iMinIndex = iIndex;
            }
        }
        if (iMinIndex != iCount)   //如果iMaxIndex值改变了说明最开始iMaxIndex里面存的元素值不是最大的
        {
            Swap(&pArray[iMinIndex], &pArray[iCount]);  //交换两个元素的值
        }
    }
}
4.算法分析
(1)时间复杂度
由上面的代码可以有两层循环,循环次数为为((N-1)+(N-2)+.......+2+1)=(N*(N-1))/2,所以时间复杂度O(N^2)。
(2)空间复杂度
空间复杂度从代码中可以很明显看出来为O(1)。
(3)稳定性
因为是跳跃式交换元素,所以是该排序算法是不稳定。
二、堆排序
1.基本思想
堆排序过程借助于完全二叉树的某些概念来实现,过程不是很复杂,但需要仔细揣摩每一步。说明算法之前先介绍什么是大根堆和小根堆。大根堆是每一个父节点都比子节点的值要大,而小根堆则相反。下面以大根堆进行说明。
基本思想是先将原始序列看成是一棵完全二叉树组成,每个元素是一个节点。首先将这个二叉树的每个节点调整为大根堆,此时根节点为整棵树中值最大的节点,然后将根节点和最后一个节点进行交换,即排序出值最大的那个元素。然后继续从根节点开始进行调整,一次排序出其他元素。
2.排序过程
这里有两点要注意的地方,我们调整大根堆时不是从最后一个节点开始,而是从最后一个节点的父节点开始进行调整;另外一点就是,已经调整好的节点不再参与下一次调整,即下一次调整时要将其剔除在外;在将整个序列调整为大根堆以后,将根节点交换到最后一个节点后,以后的每次调整只需要调整根节点就可以了。

这里写图片描述

3.代码实现
#include <stdio.h>
#include <stdlib.h>

void PrintArray(int *pArray, int iLen);
void Swap(int *iLhs, int *iRhs);
void HeapSort(int *pArray, int iLen);
void Adjust(int *pArray, int iStart, int iEnd);

int main(void)
{
    int iArray[] = {9, 7 , 6,5, 3 };
    int iLen = sizeof(iArray) / sizeof(*iArray);    //计算数组元素个数
    PrintArray(iArray, iLen);
    HeapSort(iArray, iLen);
    PrintArray(iArray, iLen);
    return 0;
}

void PrintArray(int *pArray, int iLen)
{
    int iIndex;
    for (iIndex = 0; iIndex < iLen; ++iIndex)
    {
        printf("%5d", pArray[iIndex]);
    }
    printf("\n");
}

void Swap(int *iLhs, int *iRhs)
{
    int iTemp = *iLhs;
    *iLhs = *iRhs;
    *iRhs = iTemp;
}

void Adjust(int *pArray, int iStart, int iEnd)
{
    int iIndex;
    int iTemp = pArray[iStart];
    for (iIndex = iStart * 2 + 1; iIndex < iEnd; iIndex = 2 * iIndex + 1)
    {
        //找iStart的左右孩子中值较大的那个
        if ((iIndex + 1 <= iEnd) && (pArray[iIndex] < pArray[iIndex + 1]))
        {
            ++iIndex;
        }
        if (iTemp < pArray[iIndex])
        {
            pArray[iStart] = pArray[iIndex];
            iStart = iIndex;
        }
        else
        {
            break;
        }
    }
    pArray[iStart] = iTemp;
}

void HeapSort(int *pArray, int iLen)
{
    int iIndex;
    for (iIndex = (iLen - 1 - 1) / 2; iIndex >= 0; iIndex--)
    {
        Adjust(pArray, iIndex, iLen - 1);
    }

    for (int i = 0; i < iLen - 1; ++i)
    {
        int iTemp = pArray[0];
        pArray[0] = pArray[iLen - 1 - i];
        pArray[iLen - 1 - i] = iTemp;
        Adjust(pArray, 0, iLen - 1 - i - 1);
    }
}
4.算法分析
(1)时间复杂度
堆排序中序列的每个元素都要进行调整,即N个元素要调整N次;然后每次调整过程中需要进行比较,而且比较是以二叉树形式进行,所以最多需要O(logN)次,所以时间复杂度为O(NlogN)。
(2)空间复杂度
从代码中可以看出,堆排序是就地排序,只需要借助有限的临时变量即可实现,所以空间复杂度为O(1)。
(3)稳定性
不稳定。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值