时间复杂度

只出现一次的数字

帅比萌擦擦* 提交于 2019-12-06 15:17:48
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。 说明: 你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗? public class SingleNumber { public int singleNumber(int[] nums) { int num = 0; for (int i = 0; i < nums.length; i++) { num = num ^ nums[i]; } return num; } } 总结:初看这个问题,有两点要注意 1.线性时间复杂度 2.不使用额外空间 这两点注定了调用工具类sort或者数组赋值的solutions绝对是有问题的,且时间复杂度会加倍再加倍 首先数组肯定是要遍历一遍,遍历操作中还要再进行无论是sort或者copy都会使这个时间再扩大 这一点也提示到程序的设计中,处理较大数据量的时候,arraycopy方法真的慎用,尽量扬长避短 暴露出的问题就是基本逻辑操作的不熟悉,与、或、非、异或。 习惯使用异或是很重要的,在今后的高级算法中经常会用到异或,有关异或的运用会单独总结。 来源: https://www.cnblogs.com/exigeslover/p/11992444.html

数据结构和算法分析

非 Y 不嫁゛ 提交于 2019-12-06 12:09:06
算法的渐近分析是指定义其运行时性能的数学边界/框架。使用渐近分析,我们可以很好地得出算法的最佳情况,平均情况和最坏情况。 渐近分析是输入界限,即,如果算法没有输入,则结论是在恒定时间内工作。除了“输入”之外,所有其他因素都被认为是不变的。 渐近分析是指以数学计算单位计算任何操作的运行时间。例如,一个操作的运行时间计算为 f (n),并且可以用于另一个操作,其计算为 g (n 2)。这意味着第一操作运行时间将随着 n 的增加而线性增加,并且当 n 增加时第二操作的运行时间将指数地增加。类似地,如果 n 非常小,则两个操作的运行时间几乎相同。 通常,算法所需的时间分为三种类型 - 最佳案例 - 程序执行所需的最短时间。 平均情况 - 程序执行所需的平均时间。 最坏情况 - 程序执行所需的 最 长时间。 渐近符号 以下是计算算法运行时间复杂度的常用渐近符号。 Ο符号 Ω表示法 θ表示法 大哦符号,Ο 符号Ο(n)是表示算法运行时间上限的正式方式。它测量最坏情况下的时间复杂度或算法可能需要完成的最长时间。 例如,对于函数 ** f (n)** Ο( _f_ (n)) = { _g_ (n) : there exists c > 0 and n0 such that _f_ (n) ≤ c. _g_ (n) for all n > n0. } Omega表示法,Ω 符号Ω(n

大话数据结构笔记——第二章 数据结构绪论

痞子三分冷 提交于 2019-12-06 11:00:50
第二章 算法 算法 是解决特定问题求解步骤的描述,在计算机中表现为指令的有限序列,并且每条指令表示一个或多个操作。 算法(Algorithm)这个单词最早出现在波斯数学家阿勒·花刺子密在公元825年(相当于中国的唐朝时期)所写的《印度数字算术》中。 2.1 算法的特性 算法具有5个基本特性:输入、输出、有穷性、确定性和可行性。 2.1.1 输入输出 算法具有零个或多个 输入 。 算法至少有一个或多个 输出 。 2.1.2 有穷性 算法在执行有限的步骤之后,自动结束而不会出现无限循环,并且每一个步骤在可接受的时间内完成。 2.2.3 确定性 算法的每一步骤都具有确定的含义。 2.2.4可行性 算法的每一步都必须是可行的,也就是说,每一步都能够通过执行有限次数完成。 2.2 算法设计的要求 2.2.1 正确性 算法的 正确性 是指算法至少应该具有输入、输出和加工处理无歧义性、能正确反映问题的需求、能够得到问题的正确答案。 分为 四个层次 (层次4验证复杂,代价较高,一般情况下以层次3为判断算法是否正确的标准): 1.算法程序没有语法错误。 2.算法程序对于合法的输入数据能够产生满足要求的输出结果。 3.算法程序对于非法输入数据恩能够得出满足规格说明的结果。 4.算法程序对于精心选择的,甚至刁难的测试数据都有满足要求的输出结果。 2.2.2 可读性 算法设计的另一个目的是为了便于阅读

排序算法之——归并排序和快速排序

最后都变了- 提交于 2019-12-06 10:59:24
冒泡排序、插入排序、选择排序这三种算法的时间复杂度都为 \(O(n^2)\) ,只适合小规模的数据。今天,我们来认识两种时间复杂度为 \(O(nlogn)\) 的排序算法——归并排序(Merge Sort)和快速排序(Quick Sort),他们都用到了 分治思想 ,非常巧妙。 1. 归并排序(Merge Sort)? 1.1. 归并排序算法实现 归并排序 的核心思想其实很简单,如果要排序一个数组,我们先把数组从中间分成前后两部分,然后分别对前后两部分进行排序,再将排好序的两部分数据合并在一起就可以了。 归并排序使用的是分治思想,分治也即是分而治之,将一个大问题分解为小的子问题来解决。分治算法一般都是用递归来实现的。 分治是一种解决问题的处理思想,递归是一种编程技巧 。 如果要对数组区间 [p, r] 的数据进行排序,我们先将数据拆分为两部分 [p, q] 和 [q+1, r],其中 q 为中间位置。对两部分数据排好序后,我们再将两个子数组合并在一起。当数组的起始位置小于等于终止位置时,说明此时只有一个元素,递归也就结束了。 递推公式: merge_sort(p…r) = merge(merge_sort(p…q), merge_sort(q+1…r)) 终止条件: p >= r 不用再继续分解 对两个子数组进行合并的过程如下所示,我们先建立一个临时数组

数据结构和算法之——算法复杂度分析

末鹿安然 提交于 2019-12-06 10:59:01
1. 何为数据结构?何为算法? 简单来说,数据结构就是数据的存储方式,比如数组就是把数据存在一段连续的内存上,而链表则是通过指针的关联将数据存在任意可用的内存上;栈是先进后出,队列是先进先出。 而算法则是对这些数据的操作方法,比如数据的插入、查找、删除、排序等。 二者相辅相成,互为一体,数据结构为算法服务,而算法要在指定数据结构上进行操作。 2. 复杂度分析? 学习数据结构和算法的目的是为了在实际应用的时候更加优化地利用内存,提高程序运行效率,而复杂度分析则是给我们提供一个衡量代码质量好坏的标准。 如果我们在不运行程序的情况下就可以定性知道代码的内存占用和时间消耗,这将会给我们提供一个当前程序的总体评估和未来的改进方向。 直接运行程序就可以知道算法的执行时间和占用内存,但这个过程往往会受到运行环境和数据规模的影响,因此,我们需要一个不用进行具体测试就可以粗略估计算法执行效率的方法,这就是复杂度分析。 3. 时间复杂度 3.1 大 O 复杂度表示法 int cal(int n) { int sum = 0; int i = 1; for (; i <= n; ++i) { sum = sum + i; } return sum; } 我们假设每行代码的运行时间为 t,则第一二行代码需要时间为 2 * t,第三四行代码需要时间为 2n * t,总时间为 (2n+2) * t

前置内容2:复杂度分析

空扰寡人 提交于 2019-12-06 08:42:15
注意:本文中所提到的 \(\log n\) ,都指的是 \(\log _2n\) ,另外所有除法都为整除,此外不讨论多种 \(O\) 记号写法所代表的意义之间的差别。 对于一个确定的问题,衡量一种解题方法的标准有两种:一是正确性,二是运行效率。 如果某个算法是错误的,那么没有讨论的价值。而效率的衡量是相对不容易的,因为这涉及到硬件等因素的影响。那么我们如何从这个角度衡量算法优劣呢? 主要来看看时间方面的效率。 可以发现,对于一组确定的数据,某个非随机化的程序在不同的机器上运行的时间可能不同,但是流程是相同的。换句话说,每一次的操作都是相同的。于是我们就可以用 程序执行基本操作的次数 来表示它的运行效率。注意基本操作指的是四则运算,单次函数调用,条件判断等操作;循环,递归函数等不算基本操作。 一般来说,这个基本操作的次数应该是输入数据规模 \(n\) 的函数 \(T(n)\) 。我们一般不容易直接算出 \(T(n)\) 的具体值,此时我们需要一个能够近似表达 \(T(n)\) 且较为容易计算的另一个函数 \(f(n)\) ,称为时间复杂度。记作 \(T(n)=O(f(n))\) ,或称 \(T(n)\) 与 \(f(n)\) 同阶。 如果按照百科的定义,那么 \(f(n)\) 的选取应该满足: \(\lim \limits_{n\to+\infty}\frac{T(n)}{f(n)}

莫比乌斯反演学习笔记2

倖福魔咒の 提交于 2019-12-06 08:40:44
前置知识:莫比乌斯反演学习笔记1 本文介绍一些莫比乌斯反演的灵活应用,包括杜教筛等内容。 一、整除分块(数论分块) 补充一点基础知识... 具体来说就是:如果我们要求 \(\sum \limits_{i=1}^n\lfloor\frac{n}{i}\rfloor\) ,并不需要用朴素算法 \(O(n)\) 去求,可以加快速度。 定理1:所有 \(1 \leq i \leq n\) 的 \(\lfloor\frac{n}{i}\rfloor\) 的结果最多只有 \(2\sqrt n\) 种。 证明:对于 \(i \in [1,\sqrt n]\) ,显然最多只有 \(\sqrt n\) 种取值,而对于 \(i \in [\sqrt n+1,n]\) , \(\lfloor\frac{n}{i}\rfloor\leq\sqrt n\) ,因此也最多有 \(\sqrt n\) 种取值,两个部分相加,因此最多有 \(2\sqrt n\) 种取值。 定理2:对于确定的 \(i\) ,能使得 \(\lfloor\frac{n}{x}\rfloor=\lfloor\frac{n}{i}\rfloor\) 的 \(x_{max}=\Big\lfloor\cfrac{n}{\lfloor\frac{n}{i}\rfloor}\Big\rfloor\) 。 这里选取了两种证明方法。 证法1:记 \(f

一文学会递归解题!

懵懂的女人 提交于 2019-12-06 07:14:09
前言 递归是算法中一种非常重要的思想,应用也很广,小到阶乘,再在工作中用到的比如统计文件夹大小,大到 Google 的 PageRank 算法都能看到,也是面试官很喜欢的考点 最近看了不少递归的文章,收获不小,不过我发现大部分网上的讲递归的文章都不太全面,主要的问题在于解题后大部分都没有给出相应的时间/空间复杂度,而时间/空间复杂度是算法的重要考量!递归算法的时间复杂度普遍比较难(需要用到归纳法等),换句话说,如果能解决递归的算法复杂度,其他算法题题的时间复杂度也基本不在话下。另外,递归算法的时间复杂度不少是不能接受的,如果发现算出的时间复杂度过大,则需要转换思路,看下是否有更好的解法 ,这才是根本目的,不要为了递归而递归! 本文试图从以下几个方面来讲解递归 什么是递归? 递归算法通用解决思路 实战演练(从初级到高阶) 力争让大家对递归的认知能上一个新台阶,特别会对递归的精华:时间复杂度作详细剖析,会给大家总结一套很通用的求解递归时间复杂度的套路,相信你看完肯定会有收获 什么是递归 简单地说,就是如果在函数中存在着调用函数本身的情况,这种现象就叫递归。 以阶层函数为例,如下, 在 factorial 函数中存在着 factorial(n - 1) 的调用,所以此函数是递归函数 public int factorial(int n) { if (n < =1) { return 1;

EMC 2016笔试题

ⅰ亾dé卋堺 提交于 2019-12-06 07:07:12
1.快速排序最好的时间复杂度是多少? 解答:最好和平均时间复杂度都是O(nlogn),最坏时间复杂度为O(n^2)。 快速排序的最好时间复杂度是 O(nlgn) 。快速排序的思路是:使用一个值 v 将原有序列分成小于 v 的左半部分、大于 v 的右半部分,然后对左右递归处理。理想的情况是,左右划分比较均匀,所以 有递推式: T(n) = 2T(n/2) + n ,这个递推式的通项公式 T(n) ∝ nlgn 。当然递推式退化成 T(n) = T(n-1) + n 的话,你懂的,时间复杂度就是 O(n 2 ) 了。 2.进程间通信的机制都有哪些? 解答:管道( pipe ),命名管道 (named pipe) ,信号量( semophore ), 消息队列 ( message queue ),信号 ( sinal ), 共享内存( shared memory),套接字( socket ) 。 3. 解答:答案为15次,仔细分析即可。 4. . 解答:答案为D。也就是运行时,会抛出异常。原因在于char *p =str中的str会保存在常量区,而常量区是不可改变的。所以会报 内存访问异常的 错误。 5. 答案为D,这道题考察了两点。一点是16进制的输出。一点是map的下标操作。 如果使用了std::hex的话,后面的数字会以16进制的方式 输出。 6. 解答:答案为C

哈希 二叉树

故事扮演 提交于 2019-12-06 07:00:49
我们在散列表那节中讲过,散列表的插入、删除、查找操作的时间复杂度可以做到常量级的 O(1),非常高效。而二叉查找树在比较平衡的情况下,插入、删除、查找操作时间复杂度才是 O(logn),相对散列表,好像并没有什么优势,那我们为什么还要用二叉查找树呢? 我认为有下面几个原因: 第一,散列表中的数据是无序存储的,如果要输出有序的数据,需要先进行排序。而对于二叉查找树来说,我们只需要中序遍历,就可以在 O(n) 的时间复杂度内,输出有序的数据序列。 第二,散列表扩容耗时很多,而且当遇到散列冲突时,性能不稳定,尽管二叉查找树的性能不稳定,但是在工程中,我们最常用的平衡二叉查找树的性能非常稳定,时间复杂度稳定在 O(logn)。 第三,笼统地来说,尽管散列表的查找等操作的时间复杂度是常量级的,但因为哈希冲突的存在,这个常量不一定比 logn 小,所以实际的查找速度可能不一定比 O(logn) 快。加上哈希函数的耗时,也不一定就比平衡二叉查找树的效率高。 第四,散列表的构造比二叉查找树要复杂,需要考虑的东西很多。比如散列函数的设计、冲突解决办法、扩容、缩容等。平衡二叉查找树只需要考虑平衡性这一个问题,而且这个问题的解决方案比较成熟、固定。 最后,为了避免过多的散列冲突,散列表装载因子不能太大,特别是基于开放寻址法解决冲突的散列表,不然会浪费一定的存储空间。 综合这几点