您好!
欢迎来到京东云开发者社区
登录
首页
博文
课程
大赛
工具
用户中心
开源
首页
博文
课程
大赛
工具
开源
更多
用户中心
开发者社区
>
博文
>
时间复杂度为 O(n^2) 的排序算法
分享
打开微信扫码分享
点击前往QQ分享
点击前往微博分享
点击复制链接
时间复杂度为 O(n^2) 的排序算法
wy****
2023-11-28
IP归属:北京
190浏览
对于小规模数据,我们可以选用时间复杂度为 O(n<sup>2</sup>) 的排序算法。因为时间复杂度并不代表实际代码的执行时间,它省去了低阶、系数和常数,仅代表的增长趋势,所以在小规模数据情况下, O(n<sup>2</sup>) 的排序算法可能会比 O(nlogn) 的排序算法执行效率高。不过随着数据规模增大, O(nlogn) 的排序算法是不二选择。本篇我们主要对 O(n<sup>2</sup>) 的排序算法进行介绍,在介绍之前,我们先了解一下算法特性: * 算法特性: * **稳定性**:经排序后,若等值元素之间的相对位置不变则为稳定排序算法,否则为不稳定排序算法 * **原地排序**:是否借助额外辅助空间 * **自适应性**: 自适应性排序受输入数据的影响,即最佳/平均/最差时间复杂度不等,而非自适应排序时间复杂度恒定 本篇我们将着重介绍插入排序,选择排序和冒泡排序了解即可。 ### 插入排序 插入排序的工作方式像 **整理手中的扑克牌一样**,即不断地将每一张牌插入到其他已经有序的牌中适当的位置。 插入排序的当前索引元素左侧的所有元素都是有序的:若当前索引为 i,则 \[0, i - 1] 区间内的元素始终有序,这种性质被称为 **循环不变式**,即在第一次迭代、迭代过程中和迭代结束时,这种性质始终保持不变。 不过,这些有序元素的索引位置暂时不能确定,因为它们可能需要为更小的元素腾出空间而向右移动。插入排序的代码实现如下: ```java 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` 与其左侧已排序区间元素依次比较大小,并"插入"到正确位置。插入排序对 **部分有序**(数组中每个元素距离它的最终位置都不远或数组中只有几个元素的位置不正确等情况)的数组排序效率很高。事实上,当逆序很少或数据量不大(n<sup>2</sup>和nlogn比较接近)时,插入排序可能比其他任何排序算法都要快,这也是一些编程语言的内置排序算法在针对小数据量数据排序时选择使用插入排序的原因。 算法特性: * **空间复杂度**:O(1) * **原地排序** * **稳定排序** * **自适应排序**:当数组为升序时,时间复杂度为 O(n);当数组为降序时,时间复杂度为 O(n<sup>2</sup>) ### 希尔排序 插入排序对于大规模乱序数组排序很慢,因为它只会交换相邻的元素,所以元素只能一步步地从一端移动到另一端,如果最小的元素恰好在数组的最右端,要将它移动到正确的位置需要移动 N - 1 次。 希尔排序是基于插入排序改进的排序算法,它可以交换不相邻的元素以对数组的局部进行排序,并最终用插入排序将局部有序的数组排序。它的思想是使数组中间隔为 h 的元素有序(h 有序数组),如下图为间隔为 4 的有序数组: ![希尔排序.jpg](https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2023-10-30-09-22XthOFejL6KBG9BW.jpg) 排序之初 h 较大,这样我们能将较小的元素尽可能移动到靠近左端的位置,为实现更小的 h 有序创造便利,最后一次循环时 h 为 1,便是我们熟悉的插入排序。这就是希尔排序的过程,代码实现如下: ```java 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; } } ``` 希尔排序更高效的原因是它权衡了子数组的规模和有序性,它也可以用于大型数组。排序之初,各个子数组都很短,排序之后子数组都是部分有序的,这两种情况都很适合插入排序。 *** ### 选择排序 选择排序的实现非常简单:**每次选择未排序数组中的最小值,将其放到已排序区间的末尾**,代码实现如下: ```java 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(n<sup>2</sup>) ### 冒泡排序 冒泡排序通过 **连续地比较与交换相邻元素实现排序**,每轮循环会将未被排序区间内的最大值移动到数组的最右端,这个过程就像是气泡从底部升到顶部一样,代码实现如下: ```java 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 章 * [排序算法-希尔排序](https://juejin.cn/post/7166475010121400334)
上一篇:MYSQL EXPLAIN 执行计划
下一篇:SpringMvc集成开源流量监控、限流、熔断降级、负载保护组件Sentinel
wy****
文章数
22
阅读量
2476
作者其他文章
01
高性能MySQL实战(一):表结构
最近因需求改动新增了一些数据库表,但是在定义表结构时,具体列属性的选择有些不知其所以然,索引的添加也有遗漏和不规范的地方,所以我打算为创建一个高性能表的过程以实战的形式写一个专题,以此来学习和巩固这些知识。1. 实战我使用的 MySQL 版本是 5.7,建表 DDL 语句如下所示:根据需求创建 接口调用日志 数据库表,请大家浏览具体字段的属性信息,它们有不少能够优化的点。CREATE TABLE
01
分布式服务高可用实现:复制
1. 为什么需要复制我们可以考虑如下问题:当数据量、读取或写入负载已经超过了当前服务器的处理能力,如何实现负载均衡?希望在单台服务器出现故障时仍能继续工作,这该如何实现?当服务的用户遍布全球,并希望他们访问服务时不会有较大的延迟,怎么才能统一用户的交互体验?这些问题其实都能通过 “复制” 来解决:复制,即在不同的节点上保存相同的副本,提供数据冗余。如果一些节点不可用,剩余的节点仍然可以提供数据服务
01
高性能MySQL实战(三):性能优化
这篇主要介绍对慢 SQL 优化的一些手段,而在讲解具体的优化措施之前,我想先对 EXPLAIN 进行介绍,它是我们在分析查询时必要的操作,理解了它输出结果的内容更有利于我们优化 SQL。为了方便大家的阅读,在下文中规定类似 key1 的表示二级索引,key_part1 表示联合索引的第一部分,unique_key1 则表示唯一二级索引,primary_key 表示主键索引。高性能MySQL实战(一
01
从2PC和容错共识算法讨论zookeeper中的Create请求
最近在读《数据密集型应用系统设计》,其中谈到了zookeeper对容错共识算法的应用。这让我想到之前参考的zookeeper学习资料中,误将容错共识算法写成了2PC(两阶段提交协议),所以准备以此文对共识算法和2PC做梳理和区分,也希望它能帮助像我一样对这两者有误解的同学。1. 2PC(两阶段提交协议)两阶段提交 (two-phase commit) 协议是一种用于实现 跨多个节点的原子事务(分布
wy****
文章数
22
阅读量
2476
作者其他文章
01
高性能MySQL实战(一):表结构
01
分布式服务高可用实现:复制
01
高性能MySQL实战(三):性能优化
01
从2PC和容错共识算法讨论zookeeper中的Create请求
添加企业微信
获取1V1专业服务
扫码关注
京东云开发者公众号