选择排序和堆排序(C语言实现)

选择排序(这里实现升序)

实现思想:遍历数组找出最大或最小的数,交换到数组末尾(最大)或者数组起始位置(最小),直到数组完成有序。

动图演示(演示的是找最小的)

 void Swap(int* a, int* b)//传址,才能完成交换,否则形参不会改变实参。
{
    int tmp = *a;
    *a = *b;
    *b = tmp;//交换函数
}
void SelectSort(int* a, int n)
{
    int begin = 0;
    int i = 0;
    while (begin < n)
    {
        int min = begin;//记录最小值下标,min从begin位置开始找数组未完成排序的最小值
        for (i = begin; i < n; i++)
        {
            if (a[min] > a[i])
            {
                min = i;//更新min位置
            }
        }
        Swap(&a[begin], &a[min]);
        begin++;//第一轮排序后,数组最小值已经完成排序,begin++,排序区间往后移
    }
}

这里可以优化一点,循环找最小位置的下标同时把最大位置的下标也找出来,最小的往前交换,最大的往后交换,(begin++,end--)区间改变,这样效率快一倍,但是有特殊情况要处理

void SelectSort(int* a, int n)
{
    int begin = 0;//需要排序数组开始位置
    int end = n - 1;//需要排序数组最后位置
    int i = 0;
    while (begin < end)//begin=end,一个元素不需要处理
    {
        int min = begin;//记录最小值下标
        int max = begin;//记录最大值下标
        for (i = begin; i <= end; i++)
        {
            if (a[min] > a[i])
            {
                min = i;//更新min位置
            }
            if (a[max] < a[i])
            {
                max = i;//更新mxa位置
            }
        }
        Swap(&a[begin], &a[min]);//这里先交换最小值位置与begin位置元素
        if (max == begin)//判断一下最大值是否在begin位置上
        {
            max = min;//如果是,交换后,最大值就在min下标位置
        }
        Swap(&a[end], &a[max]);
        begin++;
        end--;
    }
}
 

时间复杂度最好最坏都是O(N^2)  空间复杂度O(1)。 

堆排序 

实现思想,如果排升序,得先建一个大堆,在通过向下调节完成排序。

建堆

向下调节算法
若想将其调整为小堆,那么根结点的左右子树必须都为小堆。
若想将其调整为大堆,那么根结点的左右子树必须都为大堆。

所以我们得最后一个非叶子节点的左右孩子节点开始调堆。

思想实现(建大堆)

向下调整算法思想

1.从根结点处开始,找出左右孩子大的那个节点
2.大的节点与父亲节点比较
    若大的孩子比父亲节点还要大,则与父亲节点位置交换,并让这个节点为新的父亲节点,继续向下调整直到叶子节点为止,
 若大的孩子比父亲小,则不需处理了,调整完成,整个树已经是大堆了。

 void AdjustHeapDown(int* a, int size, int parent)
{
    int child = parent * 2 + 1;  //左孩子下标
    while (child < size)//child>=size时,已经不在堆中了
    {
        if (child + 1 < size && a[child + 1] > a[child])//child+1<size表示右孩子存在
        {
            child++;//表示右孩子的值大于左孩子的值,与父亲节点比较的值是右孩子
        }
        if (a[child] > a[parent])
        {
            Swap(&a[child], &a[parent]);//交换父亲孩子节点位置
            parent = child;//父亲迭代到该孩子节点
            child = parent * 2 + 1;//下一个需要比较的孩子节点位置
        }
        else
        {
            break;//已经满足堆的条件,跳出本次循环
        }
    }
}

有了向下调整思想,怎样将一个任意的二叉树调成堆呢 ?

因为要左右节点都满足堆的性质,才能向下调整,那我们就可以从第一个非叶子节点下标开始调节,直到调到根结束。

//n-1是最后一个节点位置,公式法求该位置的父亲节点位置,parent=(child-1)/2。
  for (int i = (n - 1 - 1) / 2; i >= 0; i--)// i=0时,左右都满足大堆,最后一次从根节点调
  {
      AdjustHeapDown(a, n, i);//n是二叉树的节点个数
  }

建堆的时间复杂度 (每层节点数*向下调节次数)

最后一层叶子节点不需要向下调节,所以只需要调到倒数第二层节点

堆排序

思想实现:

1.堆顶元素与最后一个元素交换

2.从根位置执行向下调整算法,但是此时最后一个位置不属于向下调整算法区间(此时该值已经排好),通过end--,调整区间往前移,重复执行1,2,直到end=0时(区间只有一个节点),完成排序。

void HeapSort(int* a, int n)
{

    //建堆
    for (int i = (n - 1 - 1) / 2; i >= 0; i--)
    {
        AdjustHeapDown(a, n, i);
    }

   
    int index = n - 1;//最后一个元素位置
    while (index > 0)
    {
        Swap(&a[0], &a[index]);
        AdjustHeapDown(a, index, 0);
        index--;
    }
}

时间复杂度为O(N*logN)   空间复杂度是O(1)

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值