排序算法--------插入排序和选择排序


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;
        }

    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值