时间复杂度

算法图解(二)

不打扰是莪最后的温柔 提交于 2019-12-12 07:53:22
翻看目录我能想到什么 1.数组 2.链表 3.链表数组 4.选择排序 一。数组 数组是连续的存储,内存必须分配一串连续的内存空间给他。如果没有一串连续的达到要求长度内存地址,就无法分配(基本不可能实现) 数组的访问方法:随机访问和顺序访问。 随机访问: 可以任意访问某一个数组元素 顺序访问: 必须逐个的读取元素 时间复杂度 查找:O(1) 插入:O(n) 删除:O(n) 二。链表 链表是不连续的存储。可以任意分配内存地址,但是第n个存储空间必须存储第n+1个存储空间的地址。 链表只支持顺序访问,只有访问了前一个才能获取下一个的内存地址 时间复杂度 查找:O(n) 插入:O(1) 删除:O(1) 三。链表数组 例如按照首字母存储庞大用户量的用户名 用数组存储首字母A-Z,每个数组元素指向一个链表,去存储用户名 四。选择排序 时间复杂度O(n**2) 第一次在n个中寻找最小的,第二个在(n-1)个中寻找最小的···, (n+1)*n*(1/2) 所以时间复杂度为O((n+1)*n*(1/2))=O(n**2) 最常见的排序,不断遍历整个数组寻找最小的数据,存储到另外一个新的数组中,最后输出新的数组。 写一个比较函数,找到最小值的索引号存储到数组中。 主函数中使用for循环,不停的将“最小值”加入新的数组 翻翻书看看遗漏了什么 链表数组的查找速度与插入速度与链表和数组的快慢 快->慢

数据结构之时间复杂度的计算

…衆ロ難τιáo~ 提交于 2019-12-11 02:22:20
计算过程: 用常数1取代运行时间中的所有加法常数。 在修改后的运行次数函数中,只保留最高阶项。 如果最高阶项存在且不是1,则去除与这个项目相乘的常数。得到的结果就是大O阶。 推导示例 1、常数阶 首先顺序结构的时间复杂度。下面这个算法,是利用高斯定理计算1,2,……n个数的和。 int sum = 0, n = 100; /*执行一次*/ sum = (1 + n) * n / 2; /*执行一次*/ printf("%d",sum); /*执行一次*/ 这个算法的运行次数函数是f (n) =3。 根据我们推导大0阶的方法, 第一步就是把常数项3 改为1。 在保留最高阶项时发现,它根本没有最高阶项 所以这个算法的时间复杂度为0(1)。 2、线性阶 线性阶的循环结构会复杂很多。要确定某个算法的阶次,我们常常需要确定某个特定语句或某个语句集运行的次数。因此,我们要分析算法的复杂度,关键就是要分析循环结构的运行情况。 下面这段代码,它的循环的时间复杂度为O(n), 因为循环体中的代码须要执行n次。 int i; for(i = 0; i < n; i++){ /*时间复杂度为O(1)的程序步骤序列*/ } 3、对数阶 今天某位18级同学请教我的这个问题,促使我写了这篇博客。 public class TS { public static void main(String[] args)

第九章:内部排序

被刻印的时光 ゝ 提交于 2019-12-10 21:59:13
排序:将一个数据元素(或记录)的任意序列,重新排列成一个按关键字有序的序列。 稳定性——若两个记录A和B的关键字值相等,且排序后A、B的先后次序保持不变,则称这种排序算法是稳定的。 1.插入排序 思想:每步将一个待排序的对象,按其关键码大小,插入到前面已经排好序的一组对象的适当位置上,直到对象全部插入为止。 简言之,边插入边排序,保证子序列中随时都是排好序的。 1) 直接插入排序 在已形成的有序表中线性查找,并在适当位置插入,把原来位置上的元素向后顺移 时间效率: 因为在最坏情况下,所有元素的比较次数总和为(0+1+…+n-1)→O(n^2)。 其他情况下也要考虑移动元素的次数。 故时间复杂度为O(n^2) 空间效率:仅占用1个缓冲单元——O(1) 算法的稳定性:稳定 直接插入排序算法的实现: void InsertSort ( SqList &L ) { //对顺序表L作直接插入排序 for ( i = 2; i <=L.length; i++) //假定第一个记录有序 { L.r[0]= L.r[i]; j=i-1 ; //先将待插入的元素放入“哨兵”位置 while(L[0] .key<L[j].key) { L.r[j+1]= L.r[j]; j-- ; } //只要子表元素比哨兵大就不断后移 L.r[j+1]= L.r[0]; //直到子表元素小于哨兵,将哨兵值送入 /

二分查找(上)

假如想象 提交于 2019-12-10 14:07:18
二分查找(上):如何用最省内存的方式实现快速查找功能? 1.无处不在的二分思想 2.O(logn) 惊人的查找速度 3.二分查找的递归与非递归实现 4.二分查找应用场景的局限性 1 二分查找依赖的是顺序表结构,简单点说就是数组; 2 二分查找针对的是有序数据; 3 数据量太小不适合二分查 4 数据量太大也不适合二分查找 5.总结 1.无处不在的二分思想 二分查找针对的是一个有序的数据集合,查找思想有点类似分治思想。每次都通过跟区间的中间元素对比,将待查找的区间缩小为之前的一半,直到找到要查找的元素,或者区间被缩小为 0。 2.O(logn) 惊人的查找速度 二分查找是目前为止遇到的第一个时间复杂度为 O(logn) 的算法。堆、二叉树的操作等等,它们的时间复杂度也是 O(logn)。这是一种极其高效的时间复杂度,有的时候甚至比时间复杂度是常量级 O(1) 的算法还要高效。 logn 是一个非常“恐怖”的数量级,即便 n 非常非常大,对应的 logn 也很小。比如 n 等于 2 的 32 次方,这个数很大了吧?大约是 42 亿。也就是说,如果我们在 42 亿个数据中用二分查找一个数据,最多需要比较 32 次。 3.二分查找的递归与非递归实现 最简单的情况就是有序数组中不存在重复元素: 非递归实现 public int bsearch ( int [ ] a , int n , int

堆、堆组合(对顶堆)以及堆的灵活应用

岁酱吖の 提交于 2019-12-10 02:02:06
堆的简介 转载链接(堆的基本概念) 对顶堆 简介 对顶堆是由一个大顶堆和一个小顶堆组合而成的数据结构,与传统堆维护最大数不同,对顶堆用于 动态 维护第 k k k 大的数。在对顶堆中,小根堆位于大根堆的上方,即保证了小根堆的所有数始终比大根堆大。比如维护这样一个数组 [ 1 , 4 , 6 , 8 , 10 , 14 , 16 , 18 , 23 , 55 ] [1,4,6,8,10,14,16,18,23,55] [ 1 , 4 , 6 , 8 , 1 0 , 1 4 , 1 6 , 1 8 , 2 3 , 5 5 ] ,此时需要维护第7大的数(也就是8),那么此时对顶堆的结构如下图所示。其中,红色的节点为小根堆,绿色的节点为大根堆。 其中,数字8为小根堆对顶,数字6为大根堆堆顶。可以看到,由于小根堆堆底元素小于堆顶,而大根堆堆底元素小于堆顶,这样的组合保证了整个数据结构中上方的元素永远大于下方的。 即小根堆的堆顶永远大于大根堆的堆顶 。而此时,小根堆的元素有7个,而大根堆的元素有3个,由于小根堆大于大根堆,并且小根堆堆顶元素为小根堆最小的元素,因此所求的第7大的元素即为小根堆的堆顶元8。 k k k 值修改 为了保证 k k k 值在修改后仍能保持这样特殊的数据结构,我们需要在k值发生变化时交换两个堆中的元素,不过每次交换只对相应的堆顶进行操作。比如原先的k值是7,现在将 k

数据结构导论 三 线性表-顺序存储

萝らか妹 提交于 2019-12-09 22:52:55
线性表是什么样子呢? 类似这种,可以当作生活中流水线作业是一样的。 线性表是由n(N>=0)个数据元素(结点)比如a1,a2,a3....an组成的有限序列 数据元素的个数n定义为表的长度, 当n=0时为空表(null) 当n>0时为非空表 L=(a1....an) a1称为起始点 ,an为终端结点。 对任意一堆相邻结点ai和ai+1 (1<i<n) ai是ai+1的直接前驱,ai+1是ai的直接后继 (图片来源于网络) 如果吧线性表当作火车就更加明显了,火车只有一个火车头(起始节点)一个车尾(终端结点) 火车头(起始节点)前面没有车厢(直接前驱) 但是后面又一节车厢 (直接后继),车尾(终端结点)后面没有多余的车尾(直接后继) ,但是车尾的前面有一节车厢(直接前驱)。 综上可以了解出 每一个结点且都有一个直接前驱和一个直接后继 线性表的逻辑结构特征 对于非空的线性表:线性表中结点具有一对一的关系(具体可以看上面的火车例子) 1)线性表中有且仅有一个其实结点a1,没有直接前驱,有且仅有一个直接后继a2; 2)有且仅有一个终端结点an,没有直接后继,有些仅有一个直接前驱an-1; 3)其余的内部结点ai(a<=i<=n-1)都有且仅有一个直接前驱ai-1和一个直接后继ai+1; 线性表的基本运算 初始化 Initiate 秋表长度Length 取表元 get 定位 Locate

数据结构与算法(十)---跳表的实现

余生长醉 提交于 2019-12-09 22:40:46
一、概述 什么是跳表 跳表怎么实现 二、跳表 1. 跳表的定义和实现意义 我们知道二分查找法的前提是有序数组,那么有没有类似的让链表也证婚词类似"二分"查找的算法,那就是 跳表 了:它支持快速的插入、删除、查找操作,实现方式没有红黑树那么复杂,甚至可以代替它。Redis的有序集合就是使用跳表实现的, 链表的随机访问数据的时间复杂度是O(n),我们在链表的基础上,每两个结点提取一个结点到上一级,我们把抽出来的那一级叫作 索引 或 索引层 。图中的 down 表示 down 指针,指向下一级结点。 假如我们要差早16这个元素,我们通过第一级索引进行遍历,当到达13是,发现下一索引元素的值为17,那么16就应该在13和17之间,然后从索引层的13利用down指针,查找到原始链表的13,开始往后遍历得到需要查找的元素,原来需要对遍历10个,现在只需要6个节点;效率提高了;**如果在第一级索引上,在提取一层索引,会怎样呢? 这里直接查找1-64个元素,体验下效率: 查找效率从62次到了11次,这就是跳表。 2. 跳表的时间复杂度 普通链表的时间复杂度是O(N),怎么计算跳表的时间复杂度呢? 每两个结点会抽出一个结点作为上一级索引的结点,那第一级索引的结点个数大约就是 n/2,第二级索引的结点个数大约就是 n/4,第三级索引的结点个数大约就是 n/8,依次类推,也就是说,第 k

【算法与数据结构】复杂度分析

拜拜、爱过 提交于 2019-12-09 21:36:41
本文记录了博主对算法复杂度分析,常见的几种复杂度,以及平均时间复杂度、最好/最坏时间复杂度的总结。 复杂度分析   关于算法的复杂度,我们通常采用大O来进行表示,在此我们假设每行代码的执行时间都一样,为一个单位时间,然后在这个假设的基础上进行时间、空间复杂度的分析。先分析一下上面的代码,2-4行的时间复杂度为3,紧接着有两个for循环,第一个for循环的时间复杂度为n,同时第6行的时间复杂度也为n,而内部的for循环的时间复杂度为2*n*n。总的时间复杂度则为2*n*n + 2*n+3。因此,此算法的时间复杂度为O(2*n*n + 2*n+3)。在此关于复杂度分析,有三点需要注意的:   1、 总的时间复杂度等于量级最大的那端代码的复杂度, 分析算法的时间复杂度的时候,常常忽略 常数阶 , 低阶 , 系数。因此分析一个算法的时间复杂度的时候,我们只需要关注量级最大的那段即可。    2、乘法规则: 嵌套的代码等于嵌套代码体内外执行时间的乘积 。我们可以发现在for循环的嵌套中,基本都采用了这个规则。 如下面这段代码,外部for循环的复杂度为O(3*n), 第2个for循环的复杂度则为O(2*n), 整个代码的时间复杂度为O(2*n)*O(n) + O(2*n) + 3;因此改代码的复杂度为O(n*n) 1 int cal(int n) { 2 int sum = 0; 3 int

算法 排序

痞子三分冷 提交于 2019-12-09 19:44:43
算法 排序 一、概述   排序算法可以分为内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。常见的内部排序算法有:插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。本文主要内容是介绍这八种排序算法。 二、冒泡排序:O( n^2) 三、优先队列:O( n^2):选择排序、插入排序;O(nlogn):堆排序 1、基础知识 一、位置列表   1、位置列表的抽象数据类型,可以定位到序列中任何元素的方法,并且能够执行任何位置的插入和删除操作的数据结构。位置的表示可以是数字索引(基于数组的序列)或者节点的引用(链表)!   2、面对对象需要提供强壮的数据结构和更灵活地设计数据结构,使得封装的ADT有良好的抽象!将位置抽象为一个非数字的位置的概念,即使是使用基于数组的序列!抽象的ADT(封装双向链表)实现中的位置抽象,基于数组的序列和链表的任意位置的插入和删除操作时间复杂度都是 O(1) !L.first( ).element( ):将位置作为返回值,然后在获取位置的元素。   3、 双向链表 实现位置列表类: PositionalList( )    3.1、 确认位置 :在内部,一个位置为链表的相关节点维护这引用信息,并且引用这包含指定节点的列表实例。检测到

数据的规模抽象

会有一股神秘感。 提交于 2019-12-09 19:38:35
1、将数据的规模抽象为一个n,而问题规模的大小和时间复杂度抽象为一个关于n的函数。如果在意的是时间复杂度的整体趋势或者说是时间复杂度的规模,则时间复杂度将抽象为只跟n相关的函数,去除与常数项,系数和次要项,并去最高次n项。 3、数据,分类,存储,字符串,int,组成,浮点,基本数据类型的一次封装。 4、 ADT 抽象数据类型!!!!!!!!数据的封装方式和支持的方法,只是提供接口而不在意具体的方法是如何实施的! 5、如何优化其他通用开源的框架,思路怎么操作!!! 6、算法(思想):输入(>=0个),输出(>=1个),有穷性,确定性,可行性!!! 7、列表的产生4种情况??? 9、尾部插入,append优于头部插入insert(0,i);头部取出pop() 9、 变量的类型,决定存储空间的大小需要多少字节。取的 角度,32位二进制数据,整数还是4个字符???变量决定取出来的二进制数据是什么?int,4个字节;‘ char ’用1个字节。当变量类型不一样时,由于需要保证连续存储而且每个元素占用的空间是相等的(目的是为了在有序的前提下快速定位地址,(n-1)*c),采用的是元素外置,列表存储的是元素的地址,而且表头和 元素分开存储? 元素外置 ,由于存储地址,地址是32位,需要4个字节。 10、Python中变量标识的本质,a=1,b=2,a=b;ab 不是 1和2空间的 别名