十种常见排序算法的思想、应用场景、C++代码实现及时间效率直观对比

前言:

写在这篇博客前面,最近发现自己做事学东西远没有以前的踏实,往往学过之后很快就忘记,包括基础的排序算法,虽然看过几遍,但是却没有真正掌握,到了突然需要自己抛开书本手写的时候,发现自己对它们其实挺陌生的。所以希望借写这篇博客的机会,让自己好好复习一下基本的排序算法,也希望自己从现在开始能做到学东西像之前一样踏实。

这篇博客中包含的排序算法主要有冒泡排序,插入排序,希尔排序,选择排序,堆排序,归并排序,快速排序,计数排序,基数排序,桶排序。


冒泡排序:

冒泡排序是一种很简单的排序算法,也是大多数人所学的第一种排序算法,其基本思想是“比较相邻元素,将大的元素放后边,小的放前边,每一轮比较,总有一个最大元素被‘冒’到数组最后边。”N轮比较下来,就完成了排序。

  • 时间复杂度: O(n2) O ( n 2 )
  • 空间复杂度:原址排序
  • 是否稳定:是
  • 应用场景:优化后的冒泡排序可用于当数据已经基本有序,且数据量较小时。
  • 优化措施:设置一个标志,每轮比较时,如果发现没有进行交换操作,说明数组已经有序,退出循环,停止比较。
  • 代码:
/*冒泡排序*/
void BubbleSort(vector<int>& arr) {
    int len = arr.size();
    if (len == 0)
        return;
    bool change = false;
    for (int i = 0; i < len; i++) {
        for (int j = 1; j < len - i; j++) {
            if (arr[j] < arr[j - 1]) {
                swap(arr[j], arr[j - 1]);
                change = true;
            }
        }
        if (!change)
            break;
    }
    return;
}

插入排序:

插入排序也是一种基本的排序算法,它的基本思想类似于我们抓扑克牌。开始时,我们的左手为空,然后我们每次从牌堆中拿走一张牌并插入到正确的位置;为了找到每一张牌的正确位置,需要将它从右到左与每一张牌比较,直到找到小于它的那张牌,保证左手的牌随时都是有序的,同样N轮插入操作下来,数组有序。

  • 时间复杂度: O(n2) O ( n 2 )
  • 空间复杂度:原址排序
  • 是否稳定:是
  • 应用场景:若数组基本有序且数据规模较小时,选用插入排序较好.
  • 优化措施:由于每次插入是向已排序数组中插入,可使用二分查找查找到相应位置进行插入.
  • 代码:
/*插入排序*/
void InsetionSort(vector<int>& arr) {
    int len = arr.size();
    for (int i = 1; i < len; i++) {
        int idx = i;
        while (idx > 0 && arr[idx - 1] > arr[idx]) {
            int ntmp = arr[idx];
            arr[idx] = arr[idx - 1];
            arr[idx - 1] = ntmp;
            idx--;
        }
    }
    return;
}

希尔排序:

希尔排序是对插入排序的一种改进,其改进思想来源于“序列越基本有序,插入排序效率越高”;该算法是第一批突破O(n2)时间复杂度的排序算法。基本思想:先将整个待排记录序列分割成若干列,即若干子序列,分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。

  • 时间复杂度:小于 O(n4/3) O ( n 4 / 3 )
  • 空间复杂度:原址排序
  • 是否稳定:否
  • 注:对于希尔排序涉及到一个增量步长的选择,具体选择标准可参照WIKI Gap_sequence
  • 应用场景:数据量较小且基本有序时
  • 代码:
/*希尔排序*/
void ShellSort(vector<int>& arr) {
    int len = arr.size();
    if (len == 0)
        return;
    vector<int> gaps = { 1,3,7,15,31,63,127,255,511,1023 };
    int idx = gaps.size() - 1;
    while (idx >= 0) {
        int span = gaps[idx];                                   //从第二行开始对每列数进行插入排序
        for (int i = span; i < len; i++) {
            for (int j = i; j >= span; j -= span) {
                if (arr[j] >= arr[j - span])
                    break;
                int ntmp = arr[j];
                arr[j] = arr[j - span];
                arr[j - span] = ntmp;
            }
        }
        idx--;
    }
    return;
}

选择排序:

选择排序也是一种简单直观的排序算法,它的基本思想也很简单直观:每次总是选择未排序序列中最小(或最大)的元素,放置在已排序序列最后或最前即可。

  • 时间复杂度: O(n2) O ( n 2 )
  • 空间复杂度:原址排序
  • 是否稳定:否
  • 应用场景:当数据规模较小时,选择排序性能较好
  • 优化措施:每次寻找最小或最大元素时,同时记录最小最大元素的位置,每次使用3次比较寻找两个元素的位置,而不是4次比较
  • 代码:
/*选择排序*/
void SelectionSort(
  • 13
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值