0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

时间复杂度为 O(n^2) 的排序算法

京东云 来源:京东保险 王奕 作者:京东保险 王奕 2024-10-19 16:31 次阅读

作者:京东保险 王奕龙

对于小规模数据,我们可以选用时间复杂度为 O(n2) 的排序算法。因为时间复杂度并不代表实际代码的执行时间,它省去了低阶、系数和常数,仅代表的增长趋势,所以在小规模数据情况下, O(n2) 的排序算法可能会比 O(nlogn) 的排序算法执行效率高。不过随着数据规模增大, O(nlogn) 的排序算法是不二选择。本篇我们主要对 O(n2) 的排序算法进行介绍,在介绍之前,我们先了解一下算法特性:

算法特性:

稳定性:经排序后,若等值元素之间的相对位置不变则为稳定排序算法,否则为不稳定排序算法

原地排序:是否借助额外辅助空间

自适应性: 自适应性排序受输入数据的影响,即最佳/平均/最差时间复杂度不等,而非自适应排序时间复杂度恒定

本篇我们将着重介绍插入排序,选择排序和冒泡排序了解即可。

插入排序

插入排序的工作方式像 整理手中的扑克牌一样,即不断地将每一张牌插入到其他已经有序的牌中适当的位置。

插入排序的当前索引元素左侧的所有元素都是有序的:若当前索引为 i,则 [0, i - 1] 区间内的元素始终有序,这种性质被称为 循环不变式,即在第一次迭代、迭代过程中和迭代结束时,这种性质始终保持不变。

不过,这些有序元素的索引位置暂时不能确定,因为它们可能需要为更小的元素腾出空间而向右移动。插入排序的代码实现如下:

    private void sort(int[] nums) {
        for (int i = 1; i < nums.length; i++) {
            int base = nums[i];

            int j = i - 1;
            while (j >= 0 && nums[j] > base) {
                nums[j + 1] = nums[j--];
            }
            nums[j + 1] = base;
        }
    }

它的实现逻辑是取未排序区间中的某个元素为基准数 base,将 base 与其左侧已排序区间元素依次比较大小,并"插入"到正确位置。插入排序对 部分有序(数组中每个元素距离它的最终位置都不远或数组中只有几个元素的位置不正确等情况)的数组排序效率很高。事实上,当逆序很少或数据量不大(n2和nlogn比较接近)时,插入排序可能比其他任何排序算法都要快,这也是一些编程语言的内置排序算法在针对小数据量数据排序时选择使用插入排序的原因。

算法特性:

空间复杂度:O(1)

原地排序

稳定排序

自适应排序:当数组为升序时,时间复杂度为 O(n);当数组为降序时,时间复杂度为 O(n2)

希尔排序

插入排序对于大规模乱序数组排序很慢,因为它只会交换相邻的元素,所以元素只能一步步地从一端移动到另一端,如果最小的元素恰好在数组的最右端,要将它移动到正确的位置需要移动 N - 1 次。

希尔排序是基于插入排序改进的排序算法,它可以交换不相邻的元素以对数组的局部进行排序,并最终用插入排序将局部有序的数组排序。它的思想是使数组中间隔为 h 的元素有序(h 有序数组),如下图为间隔为 4 的有序数组:

wKgaomcQeFWAejYVAAF0WDlfIVY746.jpg

排序之初 h 较大,这样我们能将较小的元素尽可能移动到靠近左端的位置,为实现更小的 h 有序创造便利,最后一次循环时 h 为 1,便是我们熟悉的插入排序。这就是希尔排序的过程,代码实现如下:

    private void sort(int[] nums) {
        int N = nums.length;
        int h = 1;
        while (h < N / 3) {
            h = 3 * h + 1;
        }

        while (h >= 1) {
            for (int i = h; i < N; i++) {
                int base = nums[i];

                int j = i - h;
                while (j >= 0 && nums[j] > base) {
                    nums[j + h] = nums[j];
                    j -= h;
                }
                nums[j + h] = base;
            }

            h /= 3;
        }
    }

希尔排序更高效的原因是它权衡了子数组的规模和有序性,它也可以用于大型数组。排序之初,各个子数组都很短,排序之后子数组都是部分有序的,这两种情况都很适合插入排序。

选择排序

选择排序的实现非常简单:每次选择未排序数组中的最小值,将其放到已排序区间的末尾,代码实现如下:

    private void sort(int[] nums) {
        for (int i = 0; i < nums.length; i++) {
            int min = i;
            for (int j = i + 1; j < nums.length; j++) {
                if (nums[j] < nums[min]) {
                    min = j;
                }
            }
            swap(nums, i, min);
        }
    }

    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }

算法特性:

空间复杂度:O(1)

原地排序

非稳定排序:会改变等值元素之间的相对位置

非自适应排序:最好/平均/最坏时间复杂度均为 O(n2)

冒泡排序

冒泡排序通过 连续地比较与交换相邻元素实现排序,每轮循环会将未被排序区间内的最大值移动到数组的最右端,这个过程就像是气泡从底部升到顶部一样,代码实现如下:

    public void sort(int[] nums) {
        for (int i = nums.length - 1; i > 0; i--) {
            // 没有发生元素交换的标志位
            boolean flag = true;
            for (int j = 0; j < i; j++) {
                if (nums[j] > nums[j + 1]) {
                    swap(nums, j, j + 1);
                    flag = false;
                }
            }

            if (flag) {
                break;
            }
        }
    }

    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }

算法特性:

空间复杂度:O(1)

原地排序

稳定排序

自适应排序:经过优化后最佳时间复杂度为 O(n)

巨人的肩膀

《算法导论 第三版》第 2.1 章

《算法 第四版》第 2.1 章

《Hello 算法》第 11 章

排序算法-希尔排序

审核编辑 黄宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 算法
    +关注

    关注

    23

    文章

    4571

    浏览量

    92320
  • 排序算法
    +关注

    关注

    0

    文章

    52

    浏览量

    10045
收藏 人收藏

    评论

    相关推荐

    时间复杂度是指什么

    原理->微机原理->软件工程,编译原理,数据库数据结构1.时间复杂度时间复杂度是指执行算法所需要的计算工作量,因为整个
    发表于 07-22 10:01

    各种排序算法时间空间复杂度、稳定性

    各种排序算法时间空间复杂度、稳定性一、排序算法分类:二、
    发表于 12-21 07:48

    常用的非比较排序算法:计数排序,基数排序,桶排序的详细资料概述

    这篇文章中我们来探讨一下常用的非比较排序算法:计数排序,基数排序,桶排序。在一定条件下,它们的时间
    的头像 发表于 06-18 15:11 7031次阅读
    常用的非比较<b class='flag-5'>排序</b><b class='flag-5'>算法</b>:计数<b class='flag-5'>排序</b>,基数<b class='flag-5'>排序</b>,桶<b class='flag-5'>排序</b>的详细资料概述

    常用排序算法分析

    一种是比较排序时间复杂度O(nlogn) ~ O(n^2
    的头像 发表于 07-13 16:13 2114次阅读

    深度剖析时间复杂度

    相信每一位录友都接触过时间复杂度,但又对时间复杂度的认识处于一种朦胧的状态,所以是时候对时间复杂度
    的头像 发表于 03-18 10:18 1822次阅读

    如何求递归算法时间复杂度

    那么我通过一道简单的面试题,模拟面试的场景,来带大家逐步分析递归算法时间复杂度,最后找出最优解,来看看同样是递归,怎么就写成了O(n)的代
    的头像 发表于 07-13 11:30 2205次阅读

    如何求递归算法时间复杂度

    相信很多同学对递归算法时间复杂度都很模糊,那么这篇Carl来给大家通透的讲一讲。
    的头像 发表于 07-13 11:33 1555次阅读

    算法之空间复杂度

    算法之空间复杂度:衡量一个算法运行需要开辟的额外空间
    的头像 发表于 08-31 10:29 1542次阅读

    常见机器学习算法的计算复杂度

    时间复杂度不是测量一个算法或一段代码在某个机器或者条件下运行所花费的时间时间复杂度一般指
    发表于 10-02 12:45 781次阅读

    算法时空复杂度分析实用指南2

    类似的,想想之前说的数据结构扩容的场景,也许`N`次操作中的某一次操作恰好触发了扩容,导致时间复杂度提高,但总的时间复杂度依然保持在`
    的头像 发表于 04-12 14:38 477次阅读
    <b class='flag-5'>算法</b>时空<b class='flag-5'>复杂度</b>分析实用指南<b class='flag-5'>2</b>

    算法时空复杂度分析实用指南(上)

    本文会篇幅较长,会涵盖如下几点: 1、Big O 表示法的几个基本特点。 2、非递归算法中的时间复杂度分析。 3、数据结构
    的头像 发表于 04-19 10:34 710次阅读
    <b class='flag-5'>算法</b>时空<b class='flag-5'>复杂度</b>分析实用指南(上)

    算法时空复杂度分析实用指南(下)

    Big O 表示法的几个基本特点。 2、非递归算法中的时间复杂度分析。 3、数据结构 API 的效率衡量方法(摊还分析)。
    的头像 发表于 04-19 10:35 594次阅读
    <b class='flag-5'>算法</b>时空<b class='flag-5'>复杂度</b>分析实用指南(下)

    常见排序算法分类

    本文将通过动态演示+代码的形式系统地总结十大经典排序算法排序算法 算法分类 —— 十种常见排序
    的头像 发表于 06-22 14:49 847次阅读
    常见<b class='flag-5'>排序</b><b class='flag-5'>算法</b>分类

    如何计算时间复杂度

    来完成,那么该算法的用处就不会太大。同样如果该算法需要若干个GB的内存,那么在大部分机器上都无法使用。 一个算法的评价主要从时间复杂度和空间
    的头像 发表于 10-13 11:19 2562次阅读
    如何计算<b class='flag-5'>时间</b><b class='flag-5'>复杂度</b>

    时间复杂度O (nlogn)的排序算法简述

    归并排序遵循分治的思想:将原问题分解几个规模较小但类似于原问题的子问题,递归地求解这些子问题,然后合并这些子问题的解来建立原问题的解。
    的头像 发表于 12-05 09:57 521次阅读
    <b class='flag-5'>时间</b><b class='flag-5'>复杂度</b><b class='flag-5'>为</b><b class='flag-5'>O</b> (nlogn)的<b class='flag-5'>排序</b><b class='flag-5'>算法</b>简述