【算法】时间复杂度以及O(N^2)的排序

目录

1.常数时间的操作

2.时间复杂度

2.1.以选择排序为例

2.2.O(n^2)从何而来

2.3.冒泡排序

2.3.1.抑或运算

2.4.插入排序

3.二分法

3.1.局部最小

4.递归

4.1.递归行为时间复杂度的估计


1.常数时间的操作

一个操作如果和样本的数据量无关,每次都是固定时间内完成的操作,叫做常数操作

时间复杂度为一个算法流程中,常数数量的一个指标,常用O来表示。具体来说,先要对一个算法流程非常熟悉,然后去写出这个算法流程中发生了多少次常数操作,进而总结出常数操作数量的表达式

在表达式中,只要高阶项,不要低阶项,也不要高阶项的系数,剩下的部分如果为f(N),那么时间复杂度为O(f(N))

评价一个算法流程的好坏,先看时间复杂度的指标,然后再分析不同样本数据下的实际运行时间,也就是“常数项时间”

  • 常数操作:int a = arr[i]; 或 +-*/ 或 位运算 等
  • 非常数操作:int b = 链表.get(i);

2.时间复杂度

2.1.以选择排序为例

时间复杂度O(N^2),额外空间复杂度O(1)

设数组长度为N,我们遍历N次,每次把遍历到的最小值放在最左边的位置上,同时把左边界向右移动一位

我们每一次找出最小的数、次小的数、次次小的数...待寻找的区间不断被压缩

这是一种基础且低效的查找方式,它的时间复杂度是O(n^2)

2.2.O(n^2)从何而来

数一数一共进行了多少次常数操作

第一次遍历时,遍历了N次,比较了N次,交换了1次

第二次遍历时,遍历了N-1次,比较了N-1次,交换了1次

......

遍历:N + N-1 + N-2 + N-3 + ...

比较:N + N-1 + N-2 + N-3 + ...

swap:N次

三者相加 = aN^2 + bN + c只要高阶项为N^2

2.3.冒泡排序

时间复杂度O(N^2),额外空间复杂度O(1)

相邻两个数字进行比较,大的数字往右移,一共遍历N-1次,第一次从第1个元素开始与相邻后一个元素比较,直到第N-1个元素与第N个元素比较完为止,此时当前数组中最大的元素一定被排序到了最后一位。接着继续遍历前N-1个元素,第二次遍历结束后,前N-1个元素的最大值一定被排序到了倒数第二位...

public static void bubbleSort(int[] arr) {if (arr == null || arr.length < 2) {return;}for (int e = arr.length-1; e > 0; e--) {for (int i = 0; i < e; i++) {if (arr[i] > arr[i+1]) {swap(arr, i, i+1);}}}
}//交换arr的i和j位置上的值
public static void swap(int[] arr, int i, int j) {arr[i] = arr[i] ^ arr[j];arr[j] = arr[i] ^ arr[j];arr[i] = arr[i] ^ arr[j];
}
2.3.1.抑或运算

相同为0,不同为1

抑或运算还可以理解为无进位相加

  • 抑或运算的性质

(1) 0^N=N N^N=0

(2) 交换律:a^b = b^a

结合律:(a^b)^c = a^(b^c)

(3)同一批数进行抑或结果永远相同

//怎么交换两个数的值
a = a^b;
b = a^b; //即b = (a^b)^b = a^(b^b) = a^0 = a
a = a^b;

a和b的值可以相等,但这样做的前提是a与b在内存里是两块独立的区域,在数组中两个指针所指向的位置不能相同

  • 与抑或有关的面试题

(1)

在一个数组中只有一种数出现了奇数次,其他的所有数都出现了偶数次,怎么找到出现了奇数次的数?要求时间复杂度O(N),空间复杂度O(1)

答:

int eor = 0;
for (int cur : arr) {eor ^= cur;
}
return eor;

(2) 在一个数组中只有两种数出现了奇数次,其他的所有数都出现了偶数次,怎么找到出现了奇数次的数?要求时间复杂度O(N),空间复杂度O(1)

  • 为什么抑或运算满足交换律与结合律

用“无进位相加”解释:一组二进制数相加的结果与什么有关,与在某个二进制位上1的个数有关,与这些1出现的次序无关。在无进位相加时,偶数个1相加的结果是0,奇数个1相加的结果是1

//假设两种出现奇数次的数是a、b,a!=b
int eor = 0;
for (int cur : arr) {eor ^= cur;
}
//eor == a^b != 0 => eor一定在某一位上等于1(int32位)
//假设eor的第x位为1
int eor_ = 0;
for (int cur : arr) {if (cur的第x位等于0) {eor_ ^= cur;}
}
//eor == a 或 eor == b 而另一个数则是eor^eor_

现在的问题是,eor的第几位是1?

我们选择最右侧的1

//找到eor最右侧的1
int rightOne = eor & (~eor + 1);
//cur的第x位为0这样表示
cur & rightOne == 0;

2.4.插入排序

时间复杂度O(N^2),额外空间复杂度O(1)

一共排序N = arr.length次,第一次排序前1个数,第二次排序前2个数...每次都把当前范围内最右边的数向左移,直到左边的数小于当前数字为止,此时该范围内的数必定有序

​​​​​​​

对于不同的数据,插入排序的时间复杂度不同

假如数据如下

时间复杂度为O(N^2)

假如数据如下

时间复杂度为O(N)

我们约定,时间复杂度是最坏情况下的算法表现,所以插入排序的时间复杂度是O(N^2)

public static void insertionSort(int[] arr) {if (arr == null || arr.length < 2) {return;}for (int i = 1; i < arr.length; i++) { //0~i做到有序for (int j = i-1; j >= 0 && arr[j] > arr[j+1]; j--) {swap(arr, j, j+1);}}
}
​
public static void swap(int[] arr, int i, int j) {arr[i] = arr[i] ^ arr[j];arr[j] = arr[i] ^ arr[j];arr[i] = arr[i] ^ arr[j];
}

3.二分法

一个有序数组找某个数是否存在

如果遍历,算法时间复杂度O(N),没有用到有序这一特性

我们每次将当前数组的中间位置元素与待查找元素比较,中间元素小,那就查找右侧子数组,反之查找左侧

每次查找都要“砍去”一半数组,数一数一共砍了几次即可得到时间复杂度O(logN)

3.1.局部最小

无序也可以用二分

长度为N的数组,规定相邻位置的元素一定不相等,如果0位置的数比1位置的数小,那么0位置的数是局部最小;如果N-1位置的数比N-2位置的数小,那么N-1位置的数为局部最小;对于中间位置i,如果i位置的数不仅比i-1位置的数小,而且比i+1位置的数小,那么i位置的数为局部最小。易得该数组必定存在至少一个局部最小

(1) 先看0位置的数是否小于1位置的数,若小则直接返回

(2) 看N-1位置的数是否小于N-2位置的数,若小则直接返回

(3) 我们取中点位置M,若[M] > [M-1],则向左侧子数组查找;若[M] < [M-1]且[M] > [M+1],则向右侧子数组查找;若都不满足,则[M]为局部最小

4.递归

一道题引入递归

问:

找到一个数组的最大值

答:

不断把当前数组拆成两个子数组,返回两个子数组的最大值中较大的那个

public static int getMax(int[] arr) {return process(arr, 0, arr.length-1);
}
​
public static int process(int[] arr, int L, int R) {if (L == R) {return arr[L];}int mid = L + ((R - l) >> 1);int leftMax = process(arr, L, mid);int rightMax = process(arr, mid+1, R);return Math.max(leftMax, rightMax);
}

对该数组进行递归

画出它的递归结构图

把递归的过程理解为一棵多叉树,每一个节点都通过子节点为自己汇总信息之后才能继续往上返回,栈空间就是整棵树的高度

4.1.递归行为时间复杂度的估计

  • master公式

满足子问题等规模的递归都可以用master公式求时间复杂度

T(N) = a*T(N/b) + O(N^d)

T(N)指的是母问题的数据量是N级别的

T(N/b)指的是子问题的规模都是N/b

a指的是子问题的调用次数

O(N^d)指的是除了调用子问题之外,剩下过程的时间复杂度

值得注意的是a/b未必等于1,因为以下式子同样满足master公式

T(N) = 2T(2N/3) + O(1)

结论:

logba < d ====> O(N^d)

logba > d ====> O(N^(logba))

logba == d ====> O((N^d)*logN)

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

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

相关文章

C# 或 .NetCore 如何使用 NPOI 导出图片到 Excel 文件

今天在本文中&#xff0c;我们将尝试使用NPOI库将图像插入到 Excel 文件的特定位置。请将以下逻辑添加到您的写作方法中&#xff0c;在 Excel 文件中添加图像&#xff08;JPEG、PNG&#xff09;,我已经有一个示例 jpeg 文件 - Read-write-excel-npoi.jpg &#xff0c;我们将尝试…

Observability:将 OpenTelemetry 添加到你的 Flask 应用程序

作者&#xff1a;来自 Elastic jessgarson 待办事项列表可以帮助管理与假期计划相关的所有购物和任务。使用 Flask&#xff0c;你可以轻松创建待办事项列表应用程序&#xff0c;并使用 Elastic 作为遥测后端&#xff0c;通过 OpenTelemetry 对其进行监控。 Flask 是一个轻量级…

使用Matplotlib显示中文的方法

1 问题提出 使用图1所示的代码进行matplotlib绘图时&#xff0c;因为其默认不支持中文&#xff0c;此时无法显示正确内容&#xff0c;如图2所示。 图1 matplotlib绘图绘图代码 图2 matplotlib无法显示中文 2 问题解决 2.1 设置全局字体 在图1所示的代码中&#xff0c;第13…

详解opencv resize之INTER_LINEAR和INTER_AREA

一。先简单介绍一下resize的用法 src&#xff1a;输入图&#xff0c; dst&#xff1a;输出图 dsize&#xff1a;输出图的宽高&#xff0c;如果dsize不为空&#xff08;即宽高都不是0&#xff09;&#xff0c;则以dsize为准进行resize。 fx, fy是放大缩小的比例&#xff0c;是…

UnityDemo-TheBrave-制作笔记

这是我跟着b站up主MStudio的视频学习制作的&#xff0c;大体上没有去做一些更新的东西&#xff0c;这里只是一个总的总结。在文章的最后&#xff0c;我会放上可以游玩该游戏的链接和exe可执行文件&#xff0c;不过没有对游戏内容进行什么加工&#xff0c;只有基本的功能实现罢了…

使用LSTM预测股票收盘价

在金融数据预测中&#xff0c;LSTM&#xff08;长短期记忆网络&#xff09;凭借其在时间序列数据建模中的优势&#xff0c;成为了分析股票价格趋势的热门选择。本篇博客将以完整的代码实现为例&#xff0c;展示如何利用LSTM网络对股票收盘价进行预测&#xff0c;并从数据处理到…

模拟SpringIOCAOP

一、IOC容器 Ioc负责创建&#xff0c;管理实例&#xff0c;向使用者提供实例&#xff0c;ioc就像一个工厂一样&#xff0c;称之为Bean工厂 1.1 Bean工厂的作用 先分析一下Bean工厂应具备的行为 1、需要一个获取实例的方法&#xff0c;根据一个参数获取对应的实例 getBean(…

预编译SQL

预编译SQL 预编译SQL是指在数据库应用程序中&#xff0c;SQL语句在执行之前已经通过某种机制&#xff08;如预编译器&#xff09;进行了解析、优化和准备&#xff0c;使得实际执行时可以直接使用优化后的执行计划&#xff0c;而不需要每次都重新解析和编译。这么说可能有一些抽…

软件测试预备知识⑥—搭建Web服务器

在软件测试的广阔领域中&#xff0c;搭建Web服务器是一项极为关键的技能。它不仅有助于模拟真实的应用环境&#xff0c;方便我们对Web应用进行全面且深入的测试&#xff0c;还能让测试人员更好地掌控测试场景&#xff0c;提升测试效率与质量。接下来&#xff0c;让我们一同深入…

计算机视觉算法实战——打电话行为检测

✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连✨ ​​​​​​​ ​​​​​​​​​​​​​​​ ​​​​​​ ​ 1. 引言✨✨ 随着智能手机的普及&#xff0c;打电话行为检测成为了计算机视…

Linux第二课:LinuxC高级 学习记录day01

0、大纲 0.1、Linux 软件安装&#xff0c;用户管理&#xff0c;进程管理&#xff0c;shell 命令&#xff0c;硬链接和软连接&#xff0c;解压和压缩&#xff0c;功能性语句&#xff0c;结构性语句&#xff0c;分文件&#xff0c;make工具&#xff0c;shell脚本 0.2、C高级 …

ISP流程--去马赛克详解

前言 本期我们将深入讨论ISP流程中的去马赛克处理。我们熟知&#xff0c;彩色图像由一个个像元组成&#xff0c;每个像元又由红、绿、蓝&#xff08;RGB&#xff09;三通道构成。而相机传感器只能感知光的强度&#xff0c;无法直接感知光谱信息&#xff0c;即只有亮暗而没有颜色…

阿里云-通义灵码:在 PyCharm 中的强大助力(下)

目录 六.通义灵码在 PyCharm 中的优势与不足 1.优势 (1).提高开发效率 (2).提升代码质量 (3).易于使用 (4).不断学习和改进 2.不足 (1).依赖网络 (2).准确性有待提高 (3).局限性 七.未来发展展望 1.提高准确性和可靠性 2.与其他工具的集成 3.智能化程度的提升 八…

开源项目stable-diffusion-webui部署及生成照片

参考链接 https://www.freedidi.com/13133.html 基础环境部署 python 官网链接 Python Release Python 3.10.6 | Python.org 下载 Python 3.10.6 版本安装包 下载好后双击 点击安装&#xff0c;这里需要选择一下&#xff0c;把环境变量加上。&#xff08;这里是默认安装到C盘…

【芯片封测学习专栏 -- 单 Die 与 多Die(Chiplet)介绍】

请阅读【嵌入式开发学习必备专栏 Cache | MMU | AMBA BUS | CoreSight | Trace32 | CoreLink | ARM GCC | CSH】 文章目录 Overview单个Die&#xff08;Monolithic Die&#xff09;多个Die&#xff08;Chiplet Architecture or Heterogeneous SoC&#xff09;如何判断一个SoC是…

Windows 安装 Docker 和 Docker Compose

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall ︱vue3-element-admin︱youlai-boot︱vue-uniapp-template &#x1f33a; 仓库主页&#xff1a; GitCode︱ Gitee ︱ Github &#x1f496; 欢迎点赞 &#x1f44d; 收藏 ⭐评论 …

java_将数据存入elasticsearch进行高效搜索

使用技术简介&#xff1a; (1) 使用Nginx实现反向代理&#xff0c;使前端可以调用多个微服务 (2) 使用nacos将多个服务管理关联起来 (3) 将数据存入elasticsearch进行高效搜索 (4) 使用消息队列rabbitmq进行消息的传递 (5) 使用 openfeign 进行多个服务之间的api调用 参…

Github Copilot学习笔记

&#xff08;一&#xff09;Prompt Engineering 利用AI工具生成prompt设计好的prompt结构使用MarkDown语法&#xff0c;按Role, Skills, Constrains, Background, Requirements和Demo这几个维度描述需求。然后收输入提示词&#xff1a;作为 [Role], 拥有 [Skills], 严格遵守 […

android分区和root

线刷包内容&#xff1a; 线刷包是一个完整的android镜像&#xff0c;不但包括android、linux和用户数据&#xff0c;还包括recovery等。当然此图中没有recovery,但是我们可以自己刷入一个。 主要分区 system.img 系统分区&#xff0c;包括linux下主要的二进制程序。 boot.img…

RabbitMQ基础(简单易懂)

RabbitMQ高级篇请看&#xff1a; RabbitMQ高级篇-CSDN博客 目录 什么是RabbitMQ&#xff1f; MQ 的核心概念 1. RabbitMQ 的核心组件 2. Exchange 的类型 3. 数据流向说明 如何安装RabbitQueue&#xff1f; WorkQueue&#xff08;工作队列&#xff09;&#xff1a; Fa…