广告
返回顶部
首页 > 资讯 > 后端开发 > JAVA >八大排序算法(含时间复杂度、空间复杂度、算法稳定性)
  • 659
分享到

八大排序算法(含时间复杂度、空间复杂度、算法稳定性)

算法排序算法c语言c++ 2023-10-19 05:10:45 659人浏览 薄情痞子
摘要

文章目录 八大排序算法(含时间复杂度、空间复杂度、算法稳定性)1、(直接)插入排序1.1、算法思想1.2、排序过程图解1.3、排序代码 2、希尔排序3、冒泡排序3.1、算法思想3.2、排

文章目录


img

八大排序算法(含时间复杂度、空间复杂度、算法稳定性)

下列算法默认都是对数组进行升序

1、(直接)插入排序

1.1、算法思想

  • 插入排序是一种简单直观的排序算法,它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

    插入排序的具体步骤如下:

    1. 从第一个元素开始,该元素可以认为已经被排序;
    2. 取出下一个元素,在已经排序的元素序列中从后向前扫描;
    3. 如果该元素(已排序)大于新元素,将该元素移到下一位置;
    4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
    5. 将新元素插入到该位置后;
    6. 重复步骤2~5。

img


1.2、排序过程图解

  • 从第一个元素开始,该元素可以认为已经被排序,取出下一个元素并记录到临时变量tmp中,在已经排序的元素序列中从后向前扫描(end--),如果该元素(已排序)大于新元素,将该元素移到下一位置,如果该元素小于等于新元素,则直接在这个元素的后面把新元素放进来。

    • 这里仅演示部分过程,其他过程自行考虑(和上述过程类似)。

1.3、排序代码

  • end指向当前要插入元素的前一个位置end+1指向当前要插入元素的位置),tmp保存当前要插入的元素,在已经排序的元素序列中从后向前扫描,找到比新元素小的元素的时候(因为有序,这个位置前面的元素比这个元素更小),直接把新元素插入到这个位置的后面。

    //插入排序void InsertSort(int *arr, int n) {    for (int i = 0; i < n - 1; ++i) {        //一趟        int end = i;        int tmp = arr[end + 1];        while (end >= 0) {            if (tmp < arr[end]) {                arr[end + 1] = arr[end];            } else {                break;            }            --end;        }        arr[end + 1] = tmp;    }}
  • 时间复杂度计算:

    • 最坏时间复杂度:数组元素原本是降序,现要求使其升序。那么每个元素需要移动或者比较的次数为:

      • 第一个元素:0
      • 第二个元素:1
      • 第三个元素:2
      • 第n个元素:n-1

      总次数:0+1+2+3+...+n-1 = n*(n-1)/2

      所以最坏时间复杂度为:O(n^2)

    • 最好时间复杂度:考虑数组原本是升序,那么所有元素需要移动或者比较的总次数为:0+1+1+...+1 = n-1。所以最好时间复杂度为O(n)

    • 平均时间复杂度:O(n^2) ----> 算法不太行

  • 空间复杂度计算:由于没有开辟额外空间来辅助数组排序,故空间复杂度为O(1)

  • 算法稳定性稳定,因为对于值相同的元素,后插入的时候不会插到相同元素的前面(tmp >= arr[end]break,即不插入)。


2、希尔排序

希尔排序详解


3、冒泡排序

3.1、算法思想

  • 冒泡排序是通过对相邻元素的比较和位置交换,使得每次遍历都可以得到剩余元素中的最大值,将其放入有序序列中最终的位置,然后下一趟排序的时候就不用去比较这个已经确定了的元素。在冒泡排序中,会依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部,就如同水底下的气泡一样逐渐向上冒。

3.2、排序过程图解

  • 每趟排序可以把一个元素”冒“到最终位置上,下一趟排序就可以少排序一个元素。

    • 这里仅演示部分过程,其他过程自行考虑(和上述过程类似)。

3.3、排序代码

  • 指针i控制每趟需要少排序的元素个数(即已经有i个元素已经在最终位置上),指针j用来比较相邻元素的大小,若相邻元素是降序,则交换这两个元素。

  • 这里定义了一个flag,用来标记每趟排序是否有交换,如果有交换,就需要继续下一趟排序,没有交换则说明数组已经有序,那么就不用继续下一趟排序!

    void Swap(int *a, int *b) {    int tmp = *a;    *a = *b;    *b = tmp;}//冒泡排序void BubbleSort(int *arr, int n) {    for (int i = 0; i < n; ++i) {        int flag = 0;        for (int j = 0; j < n - 1 - i; ++j) {            if (arr[j + 1] < arr[j]) {                flag = 1;                Swap(&arr[j], &arr[j + 1]);            }        }        if (flag == 0) {            break;        }    }}
  • 时间复杂度计算:

    • 最坏时间复杂度:考虑数组原本是降序,现在要求其升序。那么每个元素需要移动或者比较的次数为:

      • 第一趟排序:n-1
      • 第二趟排序:n-2
      • 第三趟排序:n-3
      • 第n趟排序:1

      总次数:n-1+n-2+n-3+...+1 = n*(n-1)/2

      所以最坏时间复杂度为:O(n^2)

    • 最好时间复杂度:考虑数组原本是升序。那么所有元素需要移动或者比较的次数为:

      若不使用flag:比较次数为n-1+n-2+n-3+...+1 = n*(n-1)/2次。

      使用flag:比较次数为n-1次。

    • 平均时间复杂度:O(n^2) —> 算法不太行

  • 空间复杂度计算:由于没有开辟额外空间来辅助数组排序,故空间复杂度为O(1)

  • 算法稳定性稳定,因为对于值相同的元素,每一趟排序的时候不会交换(arr[j + 1] < arr[j]才交换)。


4、(简单)选择排序

4.1、算法思想

  • 选择排序是一种简单直观的排序算法。它的工作原理如下:(优化后的选择排序–>每次都能确定当前未排序序列的最小元素和最大元素的最终位置)
    1. 在未排序序列中找到最小元素和最大元素,最小元素存放到排序序列的起始位置,最大元素存放到排序序列的末尾位置。
    2. 再从剩余未排序元素中继续寻找最小元素和最大元素,然后最小元素放到前面已排序序列的末尾最大元素放到后面已排序序列的前面
    3. 以此类推,直到所有元素均排序完毕。
  • 这里动画排序是每次选出一个最小值。(我们讲的算法更优哈哈)

4.2、排序过程图解

  • 在未排序序列中找到最小元素和最大元素,最小元素存放到排序序列的起始位置,最大元素存放到排序序列的末尾位置。

  • 再从剩余未排序元素中继续寻找最小元素和最大元素,然后最小元素放到前面已排序序列的末尾最大元素放到后面已排序序列的前面

    • 这里仅演示部分过程,其他过程自行考虑(和上述过程类似)。
  • 以此类推,直到所有元素均排序完毕。


4.3、排序代码

  • 使用minimaxi分别记录当前未排序的最小值下标和最大值下标在未排序的序列中找出最小值和最大值,然后分别交换到当前未排序的起始位置和末尾位置。需要注意的是如果当前未排序的序列中,最大值刚好在未排序序列的起始位置,那么就需要记录好这个最大值与当前未排序的序列中的最小值交换后的位置,不记录的话,那么当前maxi指向的值不一定是最大值!

    //选择排序void SelectSort(int *arr, int n) {    int mini = 0;    int maxi = 0;    int start = 0;    int end = n - 1;    while (start < end) {        for (int i = start + 1; i <= end; ++i) {            if (arr[i] > arr[maxi]) {                maxi = i;            }            if (arr[i] < arr[mini]) {                mini = i;            }        }        Swap(&arr[mini], &arr[start]);        //注意此时如果start刚好是最大值的话,就会把最大值换走了,也就是本来最大值在 0 位置,交换后换到其他位置了,所以判断一下        if (start == maxi) {            maxi = mini;//找到最大值的下标        }        Swap(&arr[maxi], &arr[end]);        //向中间靠拢        ++start;        --end;    }}
  • 时间复杂度计算:对于选择排序排序来说,没有什么最坏时间复杂度和最好时间复杂度,因为不管原数组起始是升序还是降序,元素之间的比较次数都是一样的:

    • 确定了2个元素的最终位置:n-1
    • 确定了4个元素的最终位置:n-1+n-3
    • 确定了6个元素的最终位置:n-1+n-3+n-5
    • 确定了n个元素的最终位置:n+n-3+n-5+...+1 = n*(n+1)/4 <---大约,所以时间复杂度为O(n^2)
  • 空间复杂度计算:由于没有开辟额外空间来辅助数组排序,故空间复杂度为O(1)

  • 算法稳定性不稳定,考虑序列(1,2,2),排序后序列为(1,2,2),我们发现2的相对位置发生了变化,所以是不稳定的排序算法。


5、堆排序

堆排序详解


6、快速排序

快速排序递归方法和非递归方法详解


7、归并排序

快速排序递归方法和非递归方法详解


8、计数排序

8.1、算法思想

  • 计数排序就是使用一个临时数组来记录这个原数组的元素对应这个临时数组下标出现的次数,然后再对这个临时数组0开始往后按下标出现的次数遍历。

  • 优化:对于原数组最小值较大的情况,我们可以使用对这个临时数组进行==重定位==。

    • 重定位:相当于计算机组成原理里面的将逻辑地址转化为物理地址的过程,比如序列110,110,111,120,125,122,115,118,112,118,其实它的范围就是在110~125,区间长度为16,如果我们按照这个序列的最大值来建立数组,那么需要长度为126的数组,但是这个数组的前110个空间都是0,也就是并没有用上,浪费了。但是如果创建一个长度为16的数组,下标为0~15(原数组每个元素减110,这个110是这个原数组的最小值),是不是就可以匹配这个序列的范围了呢?

      那么问题是之后遍历这个临时数组,只能得到0~15的下标,并不是我们要的110~125!其实,在遍历这个临时数组的时候,可以继续使用重定位,把这个0~15的下标重定位到110~125(每个下标都加110,这个110是这个原数组的最小值)!

8.2、排序过程图解

  • 先找到原数组的最大值和最小值,然后就可以确定临时数组的长度,然后初始化这个临时数组(全0)。

  • 然后依次遍历原数组,根据重定位,将原数组的元素减去最小值去对应临时数组的下标,并对这个下标里的元素+1

  • 遍历这个临时数组,对每个下标进行遍历,按下标对应的元素值看需要对此下标遍历几次(需要重定位回去—加上原数组的最小值)。


8.3、排序代码

  • minmax记录原数组的最小值和最大值,确定临时数组的长度(max-min+1),然后对临时数组count进行初始化,接下来就是把原数组里的元素重定位为临时数组的下标(元素值减原数组的最小值),并对此下标对应的元素+1,一直到遍历完原数组。

  • 遍历这个临时数组count,对每个下标进行遍历,按下标对应的元素值看需要对此下标遍历几次(需要重定位回去—加上原数组的最小值)。

  • 注意:这里不能找最大最小值的下标,因为在重定位回去的时候arr[j++]在变,也就是最小值下标不一定对应到最小值了!

    //计数排序void CountSort(int *arr, int n) {    //先找出数组的最大最小值    int max = arr[0];    int min = arr[0];    for (int i = 1; i < n; ++i) {        if (arr[i] > max) {            max = arr[i];        }        if (arr[i] < min) {            min = arr[i];        }    }    //节省空间,需要对元素重定位    int capacity = max - min + 1;//元素大小区间    //记录每个元素的出现次数    int *count = (int *) malloc(sizeof(int) * capacity);    if (count == NULL) {        perror("malloc error");        exit(-1);    }    memset(count, 0, sizeof(int) * capacity);    for (int i = 0; i < n; ++i) {        count[arr[i] - min]++;    }    int j = 0;    for (int i = 0; i < capacity; ++i) {        while (count[i]--) {            arr[j++] = i + min;        }    }    free(count);}
  • 时间复杂度计算:这里找最大值最小值花费时间n,遍历临时数组花费时间k(临时数组长度),所以时间复杂度为O(n+k)

  • 空间复杂度计算:使用了临时数组(临时数组长度为k),所以空间复杂度为O(k)

  • 算法稳定性稳定,因为它是利用一个数据的索引来记录元素出现的次数,而这个数组的索引就是元素的数值。当计数排序完成后,具有相同数值的元素在数组中的位置也相同,因此它们的顺序保持不变。


八大排序算法整体的时间复杂度、空间复杂度、算法稳定性等看如下表格:

排序算法平均时间复杂度最好情况最坏情况空间复杂度排序方式稳定性
(直接)插入排序O(n^2)O(n)O(n^2)O(1)内部排序稳定
希尔排序O(n^1.3)O(n^1.3)O(n^1.3)O(1)内部排序不稳定
冒泡排序O(n^2)O(n)O(n^2)O(1)内部排序稳定
(简单)选择排序O(n^2)O(n^2)O(n^2)O(1)内部排序不稳定
堆排序O(nlogn)O(nlogn)O(nlogn)O(1)内部排序不稳定
快速排序O(nlogn)O(nlogn)O(n^2)O(logn)内部排序不稳定
归并排序O(nlogn)O(nlogn)O(nlogn)O(n)外部排序稳定
计数排序O(n+k)O(n+k)O(n+k)O(k)外部排序稳定

OKOK,八大排序算法就到这里。如果你对linuxc++也感兴趣的话,可以看看我的主页哦。下面是我的GitHub主页,里面记录了我的学习代码和LeetCode的一些题的题解,有兴趣的可以看看。

Xpccccc的github主页

来源地址:https://blog.csdn.net/qq_44121078/article/details/133810151

--结束END--

本文标题: 八大排序算法(含时间复杂度、空间复杂度、算法稳定性)

本文链接: https://www.lsjlt.com/news/431837.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 八大排序算法(含时间复杂度、空间复杂度、算法稳定性)
    文章目录 八大排序算法(含时间复杂度、空间复杂度、算法稳定性)1、(直接)插入排序1.1、算法思想1.2、排序过程图解1.3、排序代码 2、希尔排序3、冒泡排序3.1、算法思想3.2、排...
    99+
    2023-10-19
    算法 排序算法 c语言 c++
  • 算法分类 ,时间复杂度 ,空间复杂度,优
        今天给大家带来一篇关于算法排序的分类,算法的时间复杂度,空间复杂度,还有怎么去优化算法的文章,喜欢的话,可以关注,有什么问题,可以评论区提问,可以与我私信,有什么好的意见,欢迎提出. 前言: 算法的复杂度分为时间复杂度与空间复杂...
    99+
    2023-01-30
    复杂度 算法 时间
  • C语言算法的时间复杂度和空间复杂度
    目录1.算法效率1.1 如何衡量一个算法的好坏1.2算法的复杂度2.时间复杂度2.1 时间复杂度的概念2.2 大O的渐进表示法2.3常见时间复杂度计算举例 3.空间复杂度4...
    99+
    2022-11-13
  • 数据结构与算法—时间复杂度和空间复杂度
    目录 1. 什么是数据结构? 2.什么是算法? 3、算法的复杂度 4、时间复杂度 (1) 时间复杂度的概念:  (2) 大O的渐进表示法:  六个例题: (3) 时间复杂度对比:  两个例题:  OJ题分析时间复杂度 5、空间复杂度 (1...
    99+
    2023-10-24
    数据结构
  • 数据结构--算法的时间复杂度和空间复杂度
    文章目录 算法效率时间复杂度时间复杂度的概念大O的渐进表示法计算实例 时间复杂度实例 常见复杂度对比例题 算法效率 算法效率是指算法在计算机上运行时所消耗的时间和资源。这是衡量算法...
    99+
    2023-09-08
    算法 数据结构 时间效率 复杂度
  • web算法的时间复杂度和空间复杂度是什么
    这篇文章主要介绍了web算法的时间复杂度和空间复杂度是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇web算法的时间复杂度和空间复杂度是什么文章都会有所收获,下面我们一起来...
    99+
    2022-10-19
  • Java算法之时间复杂度和空间复杂度的概念和计算
    目录一、算法效率二、时间复杂度2.1 时间复杂度的概念2.2 大O的渐进表示法2.3 时间复杂度的三种情况2.4 常见时间复杂度计算举例2.4.1 例子2.4.2 冒泡排序时间复杂度...
    99+
    2022-11-12
  • C语言中算法的时间复杂度和空间复杂度是什么
    这篇文章给大家分享的是有关C语言中算法的时间复杂度和空间复杂度是什么的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。1.前言1.1 什么是数据结构?数据结构(Data Structure)是计算机存储、组织数据的方...
    99+
    2023-06-29
  • C语言 超详细讲解算法的时间复杂度和空间复杂度
    目录1.前言1.1 什么是数据结构?1.2 什么是算法?2.算法效率2.1 如何衡量一个算法的好坏2.2 算法的复杂度2.3 复杂度在校招中的考察3.时间复杂度3.1 时间复杂度的概...
    99+
    2022-11-13
  • 递归算法的时间复杂度
    递归算法应该都不陌生,其实最开始遇见递归应该是在数学课上,类似于f(x)=f(x-1)+f(x+1),f(1)=1,f(2)=4,f(3)=3这种数学题大家应该见过不少,其实思想就是层层递归,最终将目标值用...
    99+
    2022-10-18
  • 递归算法时间复杂度怎么算
    递归算法的时间复杂度可以通过递归树来计算。递归树是一个树形结构,表示递归算法的执行过程。树的根节点表示原始问题,每个节点表示递归调用...
    99+
    2023-05-30
    递归算法时间复杂度 递归算法
  • Java数据结构与算法系列精讲之时间复杂度与空间复杂度
    目录概述算法的衡量标准时间复杂度最优时间复杂度平均时间复杂度最坏时间复杂度O(1)O(n)O(n^2)O(logN)空间复杂度O(1)O(n)概述 从今天开始, 小白我将带大家开启 ...
    99+
    2022-11-13
  • 如何理解算法时间复杂度
    这篇文章主要讲解了“如何理解算法时间复杂度”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何理解算法时间复杂度”吧!我们可以用下面的表达式来表示:通常主要有...
    99+
    2022-10-19
  • Java如何分析算法的时间和空间复杂度
    目录计算复杂性算法的复杂性恒定复杂性–O(1)对数复杂性–O(Log N)线性复杂度–O(N)N Log N复杂性–O(N Log N...
    99+
    2022-11-13
  • 递归算法的时间复杂度是什么
    递归算法的时间复杂度取决于递归的深度以及每次递归的时间复杂度。如果递归的深度为n,每次递归的时间复杂度为T,那么递归算法的时间复杂度...
    99+
    2023-08-28
    递归算法
  • C语言数据结构与算法之时间空间复杂度入门
    目录数据结构与算法什么是数据结构?什么是算法?分析维度大O的渐进表示法常数阶线性阶对数阶其他时间复杂度指标空间复杂度数据结构与算法 终于开始搞这块难啃的骨头了,走上这条漫漫长路之前要...
    99+
    2022-11-13
  • 算法与数据结构之如何理解时间与空间复杂度
    本篇内容介绍了“算法与数据结构之如何理解时间与空间复杂度”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!写在...
    99+
    2022-10-19
  • C语言数据结构与算法时间空间复杂度基础实践
    目录小感想时间复杂度空间复杂度小感想 今天去看了看许多人今年去各个大厂面试的面经,确实如大体所说,各大公司越来越注重性能迭代,时代需要数据结构与算法这样的考试。 一个公司的成本主要来...
    99+
    2022-11-13
  • C语言数据结构与算法时间空间复杂度实例分析
    这篇文章主要介绍“C语言数据结构与算法时间空间复杂度实例分析”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“C语言数据结构与算法时间空间复杂度实例分析”文章能帮助大家解决问题。时间复杂度来看第一个:l...
    99+
    2023-06-29
  • ASP编程算法面试:如何优化算法的时间复杂度?
    在 ASP 编程中,算法是至关重要的。它们可以帮助我们解决各种问题,从字符串匹配到图形渲染。不过,好的算法不仅需要正确性,还需要高效性。在这篇文章中,我们将探讨如何优化 ASP 编程算法的时间复杂度。 什么是时间复杂度? 在开始讨论优化算...
    99+
    2023-09-28
    编程算法 面试 path
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作