时间复杂度

常见排序算法详解(冒泡、选择、插入、快速、希尔、归并)

自闭症网瘾萝莉.ら 提交于 2019-11-28 04:07:35
一、排序算法 1、冒泡排序(Bubble Sort) 定义 :是一种简单的排序算法。它重复地遍历要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。遍历数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。 原理: 比较相邻的元素。如果第一个比第二个大(升序),就交换他们两个。 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。 针对所有的元素重复以上的步骤,除了最后一个。 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。 list1 = [12, 54, 23, 56, 67, 45, 1] def bubbleSort(): '''冒泡排序''' for i in range(len(list1) - 1, 0, -1): for j in range(i): if list1[j] > list1[j + 1]: list1[j], list1[j + 1] = list1[j + 1], list1[j] print(list1) bubbleSort() 时间复杂度: 最优时间复杂度:O(n) (表示遍历一次发现没有任何可以交换的元素,排序结束。) 最坏时间复杂度:O(n2) 稳定性:稳定 效果图: 2

Dijkstra算法堆优化详解

99封情书 提交于 2019-11-28 03:54:39
DIJ算法的堆优化 DIJ算法的时间复杂度是 \(O(n^2)\) 的,在一些题目中,这个复杂度显然不满足要求。所以我们需要继续探讨DIJ算法的优化方式。 堆优化的原理 堆优化,顾名思义,就是用堆进行优化。我们通过学习朴素DIJ算法,明白DIJ算法的实现需要从头到尾扫一遍点找出最小的点然后进行松弛。这个扫描操作就是坑害朴素DIJ算法时间复杂度的罪魁祸首。所以我们使用小根堆,用优先队列来维护这个“最小的点”。从而大大减少DIJ算法的时间复杂度。 堆优化的代码实现 说起来容易,做起来难。 我们明白了上述的大体思路之后,就可以动手写这个代码,但是我们发现这个代码有很多细节问题没有处理。 首先,我们需要往优先队列中push最短路长度,但是它一旦入队,就会被优先队列自动维护离开原来的位置,换言之,我们无法再把它与它原来的点对应上,也就是说没有办法形成点的编号到点权的映射。 我们用pair解决这个问题。 pair是C++自带的二元组。我们可以把它理解成一个有两个元素的结构体。更刺激的是,这个二元组有自带的排序方式:以第一关键字为关键字,再以第二关键字为关键字进行排序。所以,我们用二元组的first位存距离,second位存编号即可。 然后我们发现裸的优先队列其实是大根堆,我们如何让它变成小根堆呢? 有两种方法,第一种是把第一关键字取相反数,取出来的时候再取相反数。第二种是重新定义优先队列:

十大排序

点点圈 提交于 2019-11-28 00:09:35
比较排序: 冒泡:两两交换 选择:选择末序列最大(最小)值,同对应位置交换 插入:从后往前扫描有序序列 希尔排序:又叫做缩小增量排序,希尔增量:n/2 n/4 ... 1,O(n^2),Hibbard增量:1,3,7,2hk-1,O(n^1.5);下界为O(nlog(2n)) 非比较排序: 桶排序:N个待排数据,M个桶,平均每个桶[N/M]个数据的桶排序平均时间复杂度为:O(N)+O(M*(N/M)*log(N/M))=O(N+N*(logN-logM))=O(N+N*logN-N*logM)     即:平均时间复杂度为线性的O(N+C),其中C=N*(logN-logM)。如果相对于同样的N,桶数量M越大,其效率越高,最好时间复杂度达到O(N)。当然桶排序的空间复杂度为O(N+M),如果输入数据非常庞大,而桶的数量也非常多,则空间代价无疑是昂贵的。此外,桶排序是稳定的。 基数排序:n个待排数据,d个关键码,关键码取值范围radix,则时间复杂度:O(d*(n+radix)) 总结 冒泡排序是基础,每轮遍历将“最大元素”移至正确位置(“最右边”),不稳定的O(n^2); 选择排序要了解,选择排序每轮遍历将“最小(大)元素”移至正确位置(“最左(右)边”),稳定的O(n^2); 插入排序最简单,适合数据量较小的排序,依然是O(n^2); 希尔排序是插入排序升级版,不好用,为O

题解

别来无恙 提交于 2019-11-27 22:20:38
因为有些 ddl 要肝.. 把视频题解咕了..大家凑合着看文字题解吧 今年又是 Rikka 和 Yuta 在多校上秀恩爱的一年(说不定也是最后一年了.. sad 1001 Rikka with Quicksort 难度:medium 考察了大家对快速排序时间复杂度推导的熟悉程度(当然我是打表推式子的) 如果打表的话可以考虑下面这个式子,它是计算在 $m$ 处增加一个 $1$ 对答案的贡献。 $$ \begin{aligned} h_m(i) &= 0 & i < m\ h_m(i) &= 1 & i =m \ h_m(i) &= \frac{1}{i}\sum_{j=1}^{i}\left( h_m(j-1) + h_m(i-j)\right) & i > m \end{aligned} $$ 打表之后可以知道 $h_m(m)=1,h_m(n)=\frac{2(n+1)}{(m+1)(m+2)} (n > m)$。应该归纳一下就可以直接证明。所以把对应的贡献求和之后可以得到答案就是: $$ 2(n+1)H(n)-4n-\frac{2(m+2)H(m+1)-4(m+1)-m)(n+1)}{m+2} $$ 其中 $H(n)$ 表示调和级数。因为模数确定,可以用分段打表的策略来求调和级数的具体值:即把 $H(kS)$ 的值给打入程序中,这样每一次只需要计算 $kS+1$ 到 $n$

题解(临时)

爷,独闯天下 提交于 2019-11-27 22:19:36
因为有些 ddl 要肝 .. 把视频题解咕了 ..大家凑合着看文字题解吧 今年又是 Rikka 和 Yuta 在多校上秀恩爱的一年(说不定也是最后一年了 .. sad #### 1001 Rikka with Quicksort 难度: medium 考察了大家对快速排序时间复杂度推导的熟悉程度(当然我是打表推式子的) 如果打表的话可以考虑下面这个式子,它是计算在 $m$ 处增加一个 $1$ 对答案的贡献。 $$ \begin{aligned} h_m(i) &= 0 & i < m\\ h_m(i) &= 1 & i =m \\ h_m(i) &= \frac{1}{i}\sum_{j=1}^{i}\left( h_m(j-1) + h_m(i-j)\right) & i > m \end{aligned} $$ 打表之后可以知道 $h_m(m)=1,h_m(n)=\frac{2(n+1)}{(m+1)(m+2)} (n > m)$。应该归纳一下就可以直接证明。所以把对应的贡献求和之后可以得到答案就是: $$ 2(n+1)H(n)-4n-\frac{2(m+2)H(m+1)-4(m+1)-m)(n+1)}{m+2} $$ 其中 $H(n)$ 表示调和级数。因为模数确定,可以用分段打表的策略来求调和级数的具体值:即把 $H(kS)$ 的值给打入程序中,这样每一次只需要计算 $kS

玩转数据结构1-数组

|▌冷眼眸甩不掉的悲伤 提交于 2019-11-27 21:53:20
1. Java中的数组 Java中的数组是静态数组,使用场景主要是“索引有语意”的情况,比如按学号查找分数,索引为学号。Java中数组的特点主要包括: 索引从0开始 声明时需要指定数组长度 最大的优点是查询速度快,通过索引直接定位 public class Main { public static void main(String[] args) { int[] arr = new int[10]; //软编码: length for(int i = 0;i<arr.length;i++) { arr[i] = i; } int[] scores = new int[] {10,99,96}; for(int i = 0;i<scores.length;i++) { System.out.println(scores[i]); } System.out.println("-----------"); //增强for循环 for(int score: scores) { System.out.println(score); } System.out.println("-----------"); //改 scores[0] = 96; for(int score: scores) { System.out.println(score); } } } 2. 封装自己的数组类

LCA详解

a 夏天 提交于 2019-11-27 21:02:14
LCA,即最近公共祖先,在图论中应用比较广泛。 LCA的定义如下:给定一个有根树,若节点$z$同时是节点$x$和节点$y$的祖先,则称$z$是$x,y$的公共祖先;在$x,y$的所有公共祖先当中深度最大的称为$x,y$的最近公共祖先。下面给出三个最近公共祖先的例子: 显然,从上面的例子可以得出,$LCA(x,y)$即为$x,y$到根节点的路径的交汇点,也是$x$到$y$的路径上深度最小的节点。 求LCA的方法通常有三种: 向上标记法 树上倍增法 Tarjan算法 当然,求LCA还有其它方法,例如树剖等,请读者自行了解,本文主要讲解上面提到的三种方法。 向上标记法 向上标记法是求LCA最直接的方法,直接根据定义来求,单次查询的时间复杂度最坏为$O(n)$ (看起来好像还挺快的,不过什么题会只有一次查询呢) 算法流程: 从x节点向上一直走到根节点,沿途标记经过的节点 从y节点向上一直走到根节点,当第一次遇到已标记的节点时,该节点就是$LCA(x,y)$ 该方法思想是绝对简单的,实现也简单,因此在这里就不给出具体实现了。不过由于其时间复杂度过高,在实际中基本不会应用到,在这里提一下主要还是为讲解Tarjan算法做基础。 树上倍增法 树上倍增法应用非常广泛,读者可以深入地学习。用树上倍增法求LCA的时间复杂度为$O((n+m)logn)$。 树上倍增法用到了二进制拆分的思想。在求LCA时

【算法之常见的时间复杂度】

拟墨画扇 提交于 2019-11-27 18:36:03
原文: http://blog.gqylpy.com/gqy/341 "补充 空间复杂度:用来评估算法占用内存大小的式子。 什么是算法? 算法(Algorithm):一个计算过程,解决文件的方法 时间复杂度 先总结 时间复杂度是用来评估算法运行时间的一个式子(单位)。 一般来说,时间复杂度高的算法比复杂度低的算法慢。 长安的时间复杂度(按效率排序): O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n2logn) < O(n3) 不常见的时间复杂度: O(n!) O(2n) O(nn) 对应的复杂度 下面这张图和上面这张一样,其复杂度不变。 不管for循环内执行多少代码,有x层for循环,复杂度就是O(nx)。 " 原文: http://blog.gqylpy.com/gqy/341 来源: https://www.cnblogs.com/bbb001/p/11372442.html

时间复杂度、空间复杂度

落花浮王杯 提交于 2019-11-27 18:15:17
算法复杂度分为时间复杂度和空间复杂度。其作用:时间复杂度是指执行算法所需要的计算工作量;而空间复杂度是指执行这个算法所需要的内存空间。 一、时间复杂度 时间频度 一个算法执行所耗费的时间,从理论上是不能算出来的,必须上机运行测试才能知道。但我们不可能也没有必要对每个算法都上机测试,只需知道哪个算法花费的时间多,哪个算法花费的时间少就可以了。并且一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。一个算法中的语句执行次数称为语句频度或时间频度。记为T(n)。 计算方法 1. 一般情况下,算法的基本操作重复执行的次数是模块n的某一个函数f(n),因此,算法的时间复杂度记做:T(n) = O(f(n)) 分析:随着模块n的增大,算法执行的时间的增长率和f(n)的增长率成正比,所以f(n)越小,算法的时间复杂度越低,算法的效率越高。 2. 在计算时间复杂度的时候,先找出算法的基本操作,然后根据相应的各语句确定它的执行次数,再找出T(n)的同数量级(它的同数量级有以下:1、Log 2 n 、n 、nLog 2 n 、n 2 、n 3 ,2 n ,n!),找出后,f(n) = 该数量级,若T(n)/f(n)求极限可得到一常数c,则时间复杂度T(n) = O(f(n)) 例一: for(int i = 0; i < n; i++) { //一些列复杂度为O

算法时间复杂度

て烟熏妆下的殇ゞ 提交于 2019-11-27 18:14:22
算法复杂度分为时间复杂度和空间复杂度,一个好的算法应该具体执行时间短,所需空间少的特点。 随着计算机硬件和软件的提升,一个算法的执行时间是算不太精确的。只能依据统计方法对算法进行估算。我们抛开硬件和软件的因素,算法的好坏直接影响程序的运行时间。 我们看一下小例子: int value = 0; // 执行了1次 for (int i = 0; i < n; i++) { // 执行了n次 value += i; } 这个算法执行了 1 + n 次,如果n无限大,我们可以把前边的1忽略,也就是说这个算法执行了n次 时间复杂度常用大O符号表示,这个算法的时间复杂度就是O(n). 概念: 一般情况下,算法的基本操作重复执行的次数是模块n的某一函数f(n),因此,算法的时间复杂度记做 T(n) = O(f(n))。 随着模块n的增大,算法执行的时间增长率f(n)的增长率成正比,所以f(n)越小,算法 的时间复杂度越低,算法的效率越高。 计算时间复杂度 1.去掉运行时间中的所有加法常数。 2.只保留最高阶项。 3.如果最高阶项存在且不是1,去掉与这个最高阶相乘的常数得到时间复杂度 我们看一个例子 for (int i = 0; i < n; i++) { for (int j = i; j < n; j++) { // do ..... } } 当 i = 0 时 里面的fo循环执行了n次