用或不用大O来优化代码(选择排序)

news2024/11/17 17:33:37

本文内容借鉴一本我非常喜欢的书——《数据结构与算法图解》。学习之余,我决定把这本书精彩的部分摘录出来与大家分享。   

目录

写在前面

1.选择排序

2.选择排序实战

3.选择排序的实现

4.选择排序的效率

5.忽略常数

6.大O的作用

7.总结


写在前面

大 O 是一种能够比较算法效率,并告诉我们在特定环境下应采用何种算法的伟大工具。但我们不能完全依赖于它。

因为有时候即使两种算法的大 O 记法完全一样,但实际上其中一个比另一个要快得多。

本章我们就来学习如何分辨那些效率貌似一样的算法,从而选出较快的那个。


1.选择排序

上一章分析了冒泡排序算法,其效率是 O(N^2 )。现在我们再来探索另一种排序算法,选择排
序,并将它跟冒泡排序对比一下。

运用大O来给代码提速(冒泡排序)http://t.csdn.cn/PTevF选择排序的步骤如下。

(1) 从左至右检查数组的每个格子,找出值最小的那个。在此过程中,我们会用一个变量来记住检查过的数字的最小值(事实上记住的是索引,但为了看起来方便,下图就直接写出数值)。如果一个格子中的数字比记录的最小值还要小,就把变量改成该格子的索引,如图所示。

(2) 知道哪个格子的值最小之后,将该格与本次检查的起点交换。第 1 次检查的起点是索引 0,
第 2 次是索引 1,以此类推。下图展示的是第一次检查后的交换动作。

(3) 重复第(1) (2)步,直至数组排好序。 


2.选择排序实战

以数组 [4,2,7,1,3] 为例,步骤如下。

开始第 1轮检查。

首先读取索引 0。根据此算法的定义,它是目前遇到的最小值(因为现在只检查了一个格子),于是记下其索引。

第 1步:将索引 1的值 2与目前的最小值 4进行比较。

2比 4还要小,于是将目前的最小值改为 2。

第 2步:再与下一个值做比较。因为 7大于 2,所以最小值还是 2。

第 3步:将 1和目前的最小值做比较。 

1比 2还要小,于是目前的最小值更新为 1。

第 4步:比较 3和目前的最小值 1。因为现在已经走到数组尽头了,所以可以断定 1是整个
数组的最小值。 

第 5步:本次检查的起点是索引 0,不管那里的值是什么,我们都应该将最小值 1换到那里。

现在 1就排到正确的位置上了。

可以开始第 2轮检查了。 

准备工作:因为索引 0的值已符合其排位,所以这一轮从下一个格子开始,即索引 1,其值为 2,也是目前本轮所遇到的最小值。

第 6步:将 7跟目前的最小值 2进行比较。因为 2小于 7,所以最小值仍为 2。

 第 7步:将 4跟目前的最小值 2进行比较。因为 2小于 4,所以最小值仍为 2。

第 8步:将 3跟目前的最小值 2进行比较。因为 2小于 3,所以最小值仍为 2。 

又走到数组尽头了。本轮不需要做任何交换,2已在其正确位置上。于是第 2轮检查结束,现在数组如下图所示。 、

开始第 3轮检查。 

准备工作:从索引 2起,其值为 7。于是本轮目前最小值为 7。

第 9 步:比较 4 与 7。

将 4 记为目前的最小值。 

第 10 步:遇到 3,它比 4 还小。

于是 3 成了目前的最小值。

第 11 步:到数组尽头了,将 3 跟本轮起点 7 进行交换。 

于是 3 排到正确位置上了。 

虽然我们可以看到现在整个数组都有序了,但计算机是看不到的,它只会继续第 4轮检查。

准备工作:此轮检查从索引 3开始,其值 4是目前的最小值。

第 12步:比较 4和 7。 

4仍为最小值,而且它也处于本轮起点,因此无须任何交换。

因为最后一个格子左侧的那些值都已在各自的正确位置上,所以最后一格也必然正确,于是排序结束。


3.选择排序的实现

下面是用C语言实现的选择排序:

void SelectSort(int arr[], int n)//升序
{
	for (int i = 0; i < n-1; i++) //n个数,只需排好n-1个,剩下的一个自然在正确的位置上
	{
		int min = i;      //用min来记录最小值的索引(下标)         
		
        for (int j = i+1; j < n; j++)  //从i索引的后一个开始比较
		{
			if (arr[j] < arr[min])
			{
				min = j;            //找到比自己小的数的索引,并成为它
			}
		}
		if (min != i)               //如果min的值改变,则与i处的值交换
		{
			int tmp = arr[i];
			arr[i] = arr[min];
			arr[min] = tmp;
		}
	}
}

下面来进行分析:

for (int i = 0; i < n-1; i++) 

这个外层的循环代表每一轮检查。在一轮检查之初,我们会先记住目前的最小值的索引。

int min = i;

因此每轮开始时 min 都会是该轮的起点索引 i 。

注意我们实际上记录的是最小值的索引,而非最小值本身。于是,第 1 轮开始时最小值的索引是 0,到第 2 轮则是 1,以此类推。

for (int j = i+1; j < n; j++)

这个内层循环控制的是找出最小值的索引的过程。

if (arr[j] < arr[min])
{
	min = j;            
}

循环内逐个检查数组未排序的格子,若遇到比之前记录的本轮最小值还小的格子值,就将min更新为该格子的索引。

内层循环结束时,会得到未排序数值中最小值的索引。

if (min != i)
{
	int tmp = arr[i];
	arr[i] = arr[min];
	arr[min] = tmp;
}

然后再看看这个最小值是否已在正确位置,即该索引是否等于 i 。如果不是,就将 i 所指的值与最小值交换。


4.选择排序的效率

选择排序的步骤可分为两类:比较和交换。

若有 N个元素,就会有 (N - 1) + (N - 2) + (N - 3) + … + 1次比较。

但每轮的交换最多只有 1 次。如果该轮的最小值已在正确位置,就无须交换,否则要做 1 次交换。相比之下,冒泡排序在最坏情况(完全逆序)时,每次比较过后都要进行 1 次交换。

下表为冒泡排序和选择排序的并列对比。

从表中可以清晰地看到,选择排序的步数大概只有冒泡排序的一半,即选择排序比冒泡排序快一倍。 


5.忽略常数

但有趣的是,选择排序的大 O记法跟冒泡排序是一样的。

还记得我们说过,大 O记法用来表示步数与数据量的关系。所以你可能会以为步数约为 N^2的一半的选择排序,其大 O会写成 O(N ^2/ 2),以表示 N个元素需要 N ^2 / 2步。如下表所示。

但事实上,选择排序的大 O记法为 O(N^2 ),跟冒泡排序一样。这是因为大 O记法的一条重要
规则:

大 O 记法忽略常数。


6.大O的作用

尽管不能比较冒泡排序和选择排序,大 O 还是很重要的,因为它能够区分不同算法的长期增长率。当数据量达到一定程度时,O(N)的算法就会永远快过 O(N^2 ),无论这个 O(N)实际上是O(2N)还是 O(100N)。

下图为 O(N)和 O(N^2 )的对比。

这就是大 O 记法忽略常数的原因。大 O 记法只表明,对于不同分类,存在一临界点,在这一点之后,一类算法会快于另一类,并永远保持下去。至于这个点在哪里,大 O并不关心。

因此,不需要写成 O(100N),归类到 O(N)就好了。


7.总结

现在我们已经掌握了一些非常强大的算法分析手法。我们能够使用大 O去判断各种算法的效率,即便两种算法的大 O记法一样,也知道如何对比它们。不过在对比算法时,还需要考虑一个重要因素。至今我们关注的都是最坏情况下算法会跑得多慢,但其实最坏情况并不总会发生。没错,我们遇到的大都是平均情况。下一章,我们会学习怎样顾及所有情况。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/133778.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java面向对象详解(下)

文章目录&#x1f4d6;前言&#xff1a;&#x1f3c5;封装• 封装的概念• 封装的好处• 封装的核心理解&#x1f3c5;继承• 继承的概念•继承的特点● 何时使用继承&#xff1f;● 继承的形式● 继承的传递性● 继承的构造方法&#x1f9f8;super关键字&#x1f387;用途&…

【QT开发笔记-基础篇】| 第五章 绘图QPainter | 5.14 平移、旋转、缩放

本节对应的视频讲解&#xff1a;B_站_视_频 https://www.bilibili.com/video/BV1te4y1L7Mu 本节讲解平移、旋转、缩放这些变换操作 1. 关联信号槽 首先&#xff0c;在 widget.cpp 的构造中&#xff0c;为 “变换” 复选框&#xff0c;关联信号槽 // 平移、旋转、缩放 conn…

leetcode 2439. 最小化数组中的最大值

给你一个下标从 0 开始的数组 nums &#xff0c;它含有 n 个非负整数。 每一步操作中&#xff0c;你需要&#xff1a; 选择一个满足 1 < i < n 的整数 i &#xff0c;且 nums[i] > 0 。将 nums[i] 减 1 。将 nums[i - 1] 加 1 。 你可以对数组执行 任意 次上述操作&…

程序的环境与预处理 程序的编译与链接

目录 1.程序的翻译环境和执行环境 ​编辑 2.编译链接 运行环境 3.预处理 预定义符号 #define #与## 带副作用的宏参数 宏和函数的对比 命名约定 ​编辑 #undef​编辑 命令行定义 ​编辑 条件编译 文件包含 嵌套文件包含 4.其他预处理指令 1.程序的翻译环境和…

新年伊始,和大家聊聊鲜枣课堂的未来

大家好&#xff0c;我是小枣君。时间过得很快&#xff0c;转眼之间&#xff0c;2022年已经结束了。回首这一年&#xff0c;感觉自己一直都在忙&#xff0c;却想不起来到底忙了些什么。这一年&#xff0c;我的生活和工作节奏&#xff0c;一直都是混乱的。这里面&#xff0c;既有…

罗振宇2023“时间的朋友”跨年演讲原版PPT(附下载)

省时查报告-专业、及时、全面的行研报告库省时查方案-专业、及时、全面的营销策划方案库【免费下载】2022年11月份热门报告盘点2023年&#xff0c;如何科学制定年度规划&#xff1f;《底层逻辑》高清配图清华大学256页PPT元宇宙研究报告.pdf&#xff08;附下载链接&#xff09;…

软件测试[用例篇]

一. 回顾测试用例 1.测试用例基本要素 测试用例&#xff08;Test Case&#xff09;是为了实施测试而向被测试的系统提供的一组集合。 这组集合包含&#xff1a;测试环境、操作步骤、测试数据、预期结果等要素。 2.测试用例好处 测试用例可以提高测试效率&#xff08;可以减…

省时省力,高速收费站无线组网解决方案

一、行业背景随着我国高速公路里程数的不断增加&#xff0c;科技水平的不断进步&#xff0c;智能化的高速公路收费站趋势在不断的加强。例如&#xff1b;高速公路收费站智能备份系统&#xff0c;通常情况下收费站、路段分中心和省联网中心之间是需要传输收费数据记录流水、清账…

【1801. 积压订单中的订单总数】

来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 给你一个二维整数数组 orders &#xff0c;其中每个 orders[i] [pricei, amounti, orderTypei] 表示有 amounti 笔类型为 orderTypei 、价格为 pricei 的订单。 订单类型 orderTypei 可以分为两种&…

关于el-time-picker使用错误的记录

之前在紧急参与一个PC管理后台的项目&#xff0c;项目的基础架子是花裤衩大佬的vue-element-admin()vue2版本),。其中有一个需求是列表数据中数据回显时候&#xff0c;有关时间部分的数据在回显/编辑的情况下&#xff0c;提交时获取的值有问题。虽然后面解决了&#xff0c;但还…

2023年要来了。顺便分享过来后我的学开车经历

你好呀&#xff0c;读者朋友们&#xff01;我是你们的老朋友 zhen guo时光如梭&#xff0c;转眼间我这边再有1个来小时就2023年了&#xff0c;因时差&#xff0c;很多看到这里的读者朋友应该都已经进入2023年。2022年再也回不去了&#xff0c;就像曾经过去的每一年那样&#xf…

【王道操作系统】2.3.1 进程的同步与互斥

进程的同步与互斥 文章目录进程的同步与互斥1.进程同步2.进程互斥1.进程同步 同步也称为直接制约关系在多道程序环境下&#xff0c;进程是并发执行的&#xff0c;不同进程之间存在着不同的相互制约关系。为了协调进程之间的相互制约关系,如等待、传递信息等&#xff0c;引入了…

IP协议重点总结(附实例)

文章目录前言一、IP地址1.1 概念1.2 作用1.3 格式1.4 组成1.5 分类二、NAT地址转换2.1 作用2.2 转换过程2.3 NAPT端口映射2.4 现实中的栗子&#xff08;以博主的手机为例&#xff09;2.4.1 连无线WLAN的情况2.4.2 用流量上网2.5 NAT的缺陷三、子网掩码3.1 格式3.2 作用3.3 计算…

windows安装IIS服务

安装ASP的环境IIS 1、使用快捷键 【Win X】 打开系统功能菜单&#xff0c;选择【程序和功能】 2、进入【程序和功能】界面管理后&#xff0c;点击【启用或关闭windows功能】。 3、然后保证以下勾选&#xff0c;其他的默认就行&#xff0c;点击确定。如图&#xff1a; 4、出现…

vueJs中的watch与watchEffect函数

前言有时&#xff0c;我们需要在状态变化时执行一些副作用,比如:监听路由状态,更改DOM,或是根据异步操作的结果去修改另一处的状态这个时候,就需要用到监听器在组合式API中,就可以使用watch函数在每次响应式状态发生变化时触发回调函数01使用watch监视refwatch:监听某个属性的变…

2023.01/1801. 积压订单中的订单总数

1801. 积压订单中的订单总数 题意: 给你一个二维整数数组 orders &#xff0c;其中每个 orders[i] [pricei, amounti, orderTypei] 表示有 amounti 笔类型为 orderTypei 、价格为 pricei 的订单。 订单类型 orderTypei 可以分为两种&#xff1a; 0 表示这是一批采购订单 buy …

3D打印:FDM打印使用CURA4.13.1版本配置

一、前言 今天是2023年1月1日&#xff0c;新年阳历的第一天&#xff0c;在整理CSDN和写年度计划&#xff0c;对2022的总结&#xff0c;就像写一篇博客来分享一下我2022年积累的最多的一项经验&#xff0c;就是使用3D打印机&#xff0c;在2022年我先后入手了3台3d打印机&#x…

聊聊数字化转型是个啥

“国有企业首要的职责&#xff0c;就是实现国有资产保值增值。这是衡量国企工作优劣的关键&#xff01;” ——李克强 如果你开了一家制衣厂&#xff0c;雇佣了10个员工买了10台缝纫机&#xff0c;假设一天可以生产100件衣服。 做老板的你想要提高这家工厂的生产数量&#xff0…

禾元生物冲刺科创板上市:累计亏损超4亿元,贝达药业为主要股东

12月30日&#xff0c;武汉禾元生物科技股份有限公司&#xff08;下称“禾元生物”&#xff09;在上海证券交易所递交招股书&#xff0c;准备在科创板上市。本次冲刺上市&#xff0c;禾元生物计划募资35.02亿元&#xff0c;将用于植物源重组人血清白蛋白产业化基地建设项目、新药…

更多的可能

1986年12月&#xff0c;路遥的《平凡的世界》出版了&#xff0c;1992年11月17日路遥去世了&#xff0c;享年43岁&#xff0c;距今30年了……人的一生常常是白驹过隙&#xff0c;忽然而已&#xff0c;人们也常常用星空里的流星比喻&#xff0c;细细想来&#xff0c;这还算是夸张…