时间复杂度

牢记数据结构的时间复杂度 #P004#

人走茶凉 提交于 2019-12-24 21:37:09
虽然大多数情况下,Python程序都不会应用在计算密集型的场景。但是,作为一个合格的工程师,依然应该对Python内置数据类型的时间复杂度有一个基本的了解,才能够避免写出一些明显低效的代码。打个比方,我们都知道,在Python里面list是异构元素的集合,并且能够动态增长或收缩,可以通过索引和切片访问。那么,又有多少人知道,list是一个数组而不是一个链表呢。 1 时间复杂度的重要性 算法的时间复杂度是用来度量算法的运行时间,算法的空间复杂度用来度量程序占用的内存,这两个都是计算机系统中非常重要的概念。它们直接关乎程序的运行效率,其重要程度怎么强调都不为过。在这一小节中,我们将通过一个Python字符串连接的例子,来解释说明程序的时间复杂度和空间复杂度的重要性。 合格的Python工程师应该知道,Python中的字符串是不可变的。因为Python中的字符串是不可变的,在进行字符串操作时,Python每次操作都会产生一个新的字符串,新的字符串会占用一块独立的内存。因此,在操作字符串时,应该避免产生太多的中间结果。例如,下面就是一个典型的反面教材: In [1]: fruits = ['orange', 'apple', 'banana', 'pear'] In [2]: statement = fruits[0] In [3]: for item in fruits[1:]: ...

Jack Su Sparrow C# 数据结构 基础 论述

故事扮演 提交于 2019-12-24 20:10:11
本文转载连接: https://www.cnblogs.com/slf007/p/4546758.html 问题: 信息世界中,计算机是加工处理的信息的载体,在这个过程中面临着三个问题: 1.如何方便高效的组织数据 2.如何在计算机中存储数据(内存和外存) 3.如何对存储的数据进行高效的操作 目的: 我们都知道,我们都会表述一件事,老板交代你一件事情,你要陈述给你的员工,让他们明白你的意思,有些人可能简要的几句话 就把事情表达清楚,可是有的人说了一大堆才明白他说什么,这个比喻不太恰当,同样在面对同一个程序的时候我们就可以出现两 种程序:有的人写出来的程序效率很高,有的人却用复杂的方法来解决一个简单的问题。 简而言之目的有三个: 1.形成自己数据结构知识库 2.提高程序设计水平 3.提供程序设计者的基本技能 基本概念和术语 1.数据(Data):能被计算机识别的信息的载体,数值数据,声音,文字等等 2.数据元素(Data Element)和数据项(Data Item)DE:数据的实体 DI:数据的属性 最常见的是数据表的一条记录(用户) 和 字段(用户名,密码,性别等等) 3.数据对象(Data Object)性质相同的数据元素的集合 例如:{0,1,2,3,4} , {a,b,c,d} , {用户A,用户B,用户C….} 4.数据类型(Data Type)int,string等等

使用python实现冒泡、选择、插入基础排序

馋奶兔 提交于 2019-12-24 19:56:35
冒泡排序 比奇文学网 https://www.biqi.org/ 依次比较相邻两元素,若前一元素大于后一元素则交换之,直至最后一个元素即为最大; 然后重新从首元素开始重复同样的操作,直至倒数第二个元素即为次大元素; 依次类推。如同水中的气泡,依次将最大或最小元素气泡浮出水面。 实现 # 冒泡排序 def bubble_sort(li): # 建立一个标识符 flag = False for i in range(len(li)-1 ): for j in range(len(li)-i-1 ): if li[j] > li[j+1 ]: li[j], li[j +1] = li[j+1 ], li[j] flag = True # 如果没进行交换,则本身有序,直接break if not flag: break return li 算法分析 平均时间复杂度:O(n 2 ),标准的内外两层循环 最好时间复杂度:O(n),如果有序,那么第一趟就ok了 最坏时间复杂度:O(n 2 ) 空间复杂度:O(1) 稳定性:稳定的 选择排序 首先初始化最小元素索引值为首元素,依次遍历待排序数列,若遇到小于该最小索引位置处的元素则刷新最小索引为该较小元素的位置,直至遇到尾元素,结束一次遍历,并将最小索引处元素与首元素交换; 然后,初始化最小索引值为第二个待排序数列元素位置,同样的操作

排序优化——模拟栈及三路快排

久未见 提交于 2019-12-24 14:00:09
几乎所有的编程语言都会提供排序函数,比如 C 语言的 qsort(), C++ STL 中的 sort(),这些排序函数是如何实现的呢? 1. 如何选择合适的排序算法? 如果要实现一个通用的高效率的排序函数,我们应该选择那种排序算法呢? 各种排序算法的特点如下所示。 线性排序算法的时间复杂度比较低,适用场景特殊,因此不适合作为通用的排序函数。 小规模数据可以选择时间复杂度为 \(O(n^2)\) 的算法,大规模数据选择时间复杂度为 \(O(nlogn)\) 的算法则会更加高效。为了兼顾任意规模的数据,一般会首选复杂度为 \(O(nlogn)\) 的算法来实现排序函数。 归并排序虽然最好情况、最坏情况和平均情况下时间复杂度都可以做到 \(O(nlogn)\) ,但它不是原地排序算法,空间复杂度为 \(O(n)\) ,排序的时候需要的额外空间和源数据一般大,空间消耗过高。 2. 如何优化快速排序? 快速排序最坏情况下时间复杂度退化为 \(O(n^2)\) ,我们怎样来避免这种情况的发生呢? 实际上,这种 \(O(n^2)\) 复杂度出现的主要原因还是 分区点选取得不合理 。 理想的分区点应该是,被分区点分开的两个区间,数据的数量差不多。 2.1. 分区点优化问题 三数取中法 。从待排序数据首、尾、中分别取出一个数,然后对比大小,以这三个数的中间值作为分区点。 如果排序数据比较多,可能要

吃可爱长大的小学妹 提交于 2019-12-24 04:21:05
栈可以理解为一个有底的瓶子, 先进后出,后进先出,这是栈的特点 。在栈的操作特性上来看,栈是一种“操作受限”的线性表,只允许在一端插入、删除数据。当然,也不能简单将其定性为“局限性”。因为特定的数据结构是为了对应特定场景,数组、链表暴露了很多操作接口显得比较灵活,但是使用起来可控性上不强,容易出问题。 当某个数据集合只涉及在一端插入和删除数据、且满足先进先出、后进后出的特点时,首选栈。 实现 栈可以使用数组、链表来实现,数组实现的栈叫做顺序栈,链表实现的栈叫做链式栈。 基于数组实现: public class ArrayStack { private String [ ] items ; //数组 private int count ; //栈中元素个数 private int n ; //栈大小 //构造中初始化数组,申请大小为n的数组空间 public ArrayStack ( int n ) { this . n = n ; this . items = new String [ n ] ; this . count = 0 ; } //入栈操作 public boolean push ( String item ) { //数组空间不足,入栈失败 if ( count == n ) return false ; //将item放在下标为count的位置 items [

第k大数问题

雨燕双飞 提交于 2019-12-24 03:59:33
解法1: 我们可以对这个乱序数组按照从大到小先行排序,然后取出前k大,总的时间复杂度为O(n*logn + k)。 解法2: 利用选择排序或交互排序,K次选择后即可得到第k大的数。总的时间复杂度为O(n*k) 解法3: 利用快速排序的思想,从数组S中随机找出一个元素X,把数组分为两部分Sa和Sb。Sa中的元素大于等于X,Sb中元素小于X。这时有两种情况: 1. Sa中元素的个数小于k,则Sb中的第k-|Sa|个元素即为第k大数; 2. Sa中元素的个数大于等于k,则返回Sa中的第k大数。时间复杂度近似为O(n) 解法4: 二分[Smin,Smax]查找结果X,统计X在数组中出现,且整个数组中比X大的数目为k-1的数即为第k大数。时间复杂度平均情况为O(n*logn) 解法5:用O(4*n)的方法对原数组建最大堆,然后pop出k次即可。时间复杂度为O(4*n + k*logn) 解法6:维护一个k大小的最小堆,对于数组中的每一个元素判断与堆顶的大小,若堆顶较大,则不管,否则,弹出堆顶,将当前值插入到堆中。时间复杂度O(n * logk) 解法7:利用hash保存数组中元素Si出现的次数,利用计数排序的思想,线性从大到小扫描过程中,前面有k-1个数则为第k大数,平均情况下时间复杂度O(n) 详细出处参考: http://www.jb51.net/article/37321.htm 来源

任意图匹配 带花树模版

老子叫甜甜 提交于 2019-12-24 02:48:03
匹配就是一个图中一堆没有端点的边的集合,求最大匹配就是求这个边集最大有多少条边。 无论是任意图还是二分图,都有以下定理: 当前匹配是最大匹配当且仅当不存在增广路。 增广路的定义就是,一条包含奇数条边的路径,最前和最后的两条边都是非匹配边,且对于路径非两端的点,都连接着一条匹配边和非匹配边。 求图的匹配的算法就是不断地找增广路,把增广路上的匹配边变成非匹配边,非匹配边变成匹配边。 对于二分图来说,只要从一个没有被匹配到的点开始bfs(dfs)一下就能找到增广路(如果确实有增广路)。 但是对于任意图来说,从一个没有被匹配到的点开始bfs(dfs)不一定能找到,能不能找到取决于遍历的顺序。 于是,为了使任意图可以有匹配,带花树就出现。带花树其实就是一棵带着花的树。 任意图中搜索顺序对找增广路有影响主要是因为任意图中有奇数环,在bfs树上出现的这些奇数环就叫做花。 由于这些花不太和谐,所以要进行处理,于是就把这些花缩成一个点,然后继续bfs。 总的来说,带花树的算法就是按照二分图匹配中那样找增广路,遇到花就缩起来。 找到花要把花缩成一点,就要把整个花找出来,这里暴力找一下好了,暴力用的时间等于花上点的个数,由于找完后花就缩起来了,所以找一条增广路中缩点的总时间复杂度是不超过O(V)的。 枚举增广路的初始点是O(V),找一条增广路的时间复杂度是O(E)(邻接矩阵O(V^2))的

复杂度分析

一曲冷凌霜 提交于 2019-12-24 02:23:15
内容整理自 《数据结构与算法之美》 整理: 思考: 分析下面这个 add() 函数的时间复杂度 // 全局变量,大小为10的数组array,长度len,下标i。 int array[] = new int[10]; int len = 10; int i = 0; // 往数组中添加一个元素 void add(int element) { if (i >= len) { // 数组空间不够了 // 重新申请一个2倍大小的数组空间 int new_array[] = new int[len*2]; // 把原来array数组中的数据依次copy到new_array for (int j = 0; j < len; ++j) { new_array[j] = array[j]; } // new_array复制给array,array现在大小就是2倍len了 array = new_array; len = 2 * len; } // 将element放到下标为i的位置,下标i加一 array[i] = element; ++i; } 1.最好情况时间复杂度 最理想的情况下,数组中有空间,直接将数据添加到数组即可,所以最好情况时间复杂度为 O(1)。 2.最坏情况时间复杂度 最糟糕的情况下,数组中没有空间,需要for循环进行数组的copy,然后再添加数据,所以最坏情况时间复杂度为 O

算法的时间和空间复杂度

∥☆過路亽.° 提交于 2019-12-23 15:23:54
一:算法概念 算法(Algorithm)指用来操作数据、解决程序问题的一组方法。对于同一个问题,使用不同的算法,也许最终得到的结果是一样的,但在过程中消耗的资源和时间却会有很大的区别。 主要从算法所占用的「时间」和「空间」两个维度去考量。 时间维度:是指执行当前算法所消耗的时间,我们通常用「时间复杂度」来描述。 空间维度:是指执行当前算法需要占用多少内存空间,我们通常用「空间复杂度」来描述。 算法的效率主要是看它的时间复杂度和空间复杂度情况。然而,有的时候时间和空间却又是鱼和熊掌,不可兼得的,使用者需要根据实际需求取舍。 比如ThreadLocal就是空间换取时间,多线程就是时间换空间,虽然它们不涉及算法,但思想是想通的。 一:时间复杂度 算法的时间复杂度反映了程序执行时间随输入规模增长而增长的量级,在很大程度上能很好反映出算法的优劣与否。 为了简便,我们一般在计算时间复杂度往往选取最简单的f(n)表示。例如: ,一般都只用 表示就可以了。也就是说,两个算法的时间频度不一样,但很有可能拥有相同的时间复杂度。 例如: 与 它们的频度不同,但时间复杂度相同,都为 。 常见的算法时间复杂度由小到大依次为: 。 时间复杂度的分类: 最坏时间复杂度:输入数据状态最不理想情况下的时间复杂度,也就是算法时间复杂度的上界。若没有特别声明,时间复杂度就是指最坏时间复杂度。 平均时间复杂度

算法(1)--时间和空间复杂度

不问归期 提交于 2019-12-23 15:23:32
算法(1)--时间和空间复杂度 初识 算法定义 算法是独立存在的一种解决问题的方法和思想: 求解一个问题步骤的描述 是求解问题的方法 它是指令的有限序列 其中每条指令表示一个或者多个操作 对于算法而言,实现的语言并不重要,重要的是思想 算法特性 确定性:无二义 有穷性:合适时间内可以执行 输入项 输出项 可行性:算法的每一步都是可行的 复杂度 时间复杂度 定义 ​ 一般情况下,算法中 基本操作重复执行的次数 是 问题规模n 的某个函数,用 T(n) 表示 (语句频度) ,若有某个辅助函数 f(n) ,使得当 n 趋近于 无穷大时 , T(n)/f(n) 的 极限值 为 不等于零的常数 ,则称 f(n) 是 T(n) 的 同数量级函数 。记作 T(n)=O(f(n)) ,称 O(f(n)) 为算法的渐进时间复杂度( O是数量级的符号 ),简称 时间复杂度 。 求解步骤 求解算法时间复杂度的步骤: 找出算法中的基本语句,计算 基本操作执行次数 T(n) # 基本操作即算法中的每条语句(以;号作为分割),语句的执行次数也叫做语句的频度。在做算法分析时,一般默认为考虑最坏的情况。 计算基本语句的执行次数 T(n) 的 数量级 # 忽略常量、低次幂和最高次幂的系数,令f(n)=T(n)的数量级 用大 O 来表示 时间复杂度 # 当n趋近于无穷大时,如果lim(T(n)/f(n)