时间复杂度

1044 Shopping in Mars (25分)/二分法

自闭症网瘾萝莉.ら 提交于 2020-01-26 20:28:58
题目链接 一开始是用的简单模拟的方法做的,有两个测试点超时。于是把cin、cout改为了scanf、printf,可是仍然超时。主要是套了两层循环,时间复杂度为O(n^2)。 看了柳神代码,说要用二分法,于是我又想了想。可是还是每次把求sum的工作放在了函数里面,时间复杂度还是O(n^2)。然后才想到可以在输入时求从第一个数累计求和到当前的数,保存在数组中,这样方便于求子串的和。 用二分法的话这道题的时间复杂度应该是O(nlogn),快了很多。 AC代码 # include <iostream> # include <cstring> using namespace std ; int sum [ 100005 ] , an [ 100005 ] ; int min = 999999999 ; int solve ( int left , int right , int & m ) { int mid , L = left ; while ( left < right ) { mid = ( left + right ) / 2 ; if ( sum [ mid ] - sum [ L - 1 ] >= m ) right = mid ; else left = mid + 1 ; } return left ; } int main ( ) { int n , m ; scanf

线段树之入门篇

断了今生、忘了曾经 提交于 2020-01-26 15:40:07
线段树 (interval tree) 是把区间逐次二分得到的一树状结构。它反映了包含归并排序在内的非常多分治算法的问题求解方式。 上图是一棵典型的线段树。它对区间[1,10]进行切割。直到单个点。这棵树的特点 是: 1. 每一层都是区间[a, b]的一个划分。记 L = b - a 2. 一共同拥有log2L层 3. 给定一个点p。从根到叶子p上的全部区间都包括点p。且其它区间都不包括点p。 4. 给定一个区间[l; r],能够把它分解为不超过2log2 L条不相交线段的并。 当中第四点并非非常显然。图8.1显示了[2, 8]的分解方式,深灰色结点为分解后的 区间,浅灰色结点是递归分解过程中经过的结点。 为了叙述方便,以下称树中的结点 所相应的区间为树中区间。   从第3点和第4点能够得出结论:改动一个点仅仅用改动log2 L个树中区间信息。而统计一个区间仅仅须要累加2log2 L个树中区间的信息,且訪问的总结点数是O(log L)的。 两个操作都非常easy实现。    动态统计问题I 有一个包括n个元素的整数数组A,每次能够改动一个元素,也能够询问某一个区间[l; r]内全部元素的和。怎样设计算法,使得改动和询问操作的时间复杂度尽量低?   方法一 直接做, 则改动操作是O(1)的, 但询问须要进行累加, 时间复杂度为O(r ¡ l),最坏情况为O(n)。   方法二

线段树入门

♀尐吖头ヾ 提交于 2020-01-26 15:39:07
 // 转载自: http://www.cnblogs.com/superbin/archive/2010/08/02/1790467.html 感觉讲的很详细。也很实用。果断转~感谢楼主~~~ 线段树入门 线段树 (interval tree) 是把区间逐次二分得到的一树状结构,它反映了包括归并排序在内的很多分治算法的问题求解方式。 上图是一棵典型的线段树,它对区间[1,10]进行分割,直到单个点。这棵树的特点 是: 1. 每一层都是区间[a, b]的一个划分,记 L = b - a 2. 一共有log2L层 3. 给定一个点p,从根到叶子p上的所有区间都包含点p,且其他区间都不包含点p。 4. 给定一个区间[l; r],可以把它分解为不超过2log2 L条不相交线段的并。 其中第四点并不是很显然,图8.1显示了[2, 8]的分解方式,深灰色结点为分解后的 区间,浅灰色结点是递归分解过程中经过的结点。为了叙述方便,下面称树中的结点 所对应的区间为树中区间。   从第3点和第4点可以得出结论:修改一个点只用修改log2 L个树中区间信息,而统计一个区间只需要累加2log2 L个树中区间的信息,且访问的总结点数是O(log L)的。两个操作都很容易实现。    动态统计问题I 有一个包含n个元素的整数数组A,每次可以修改一个元素,也可以询问某一个区间[l; r]内所有元素的和

UVA 11464 Even Parity【暴力枚举】

拟墨画扇 提交于 2020-01-26 07:18:42
传送门 这是我第2次遇到相同类型的题目了。 如果暴力搜索时间复杂度是指数的指数。 但这类题目有个特点就是可以通过已有的条件推出其他的解(我们可以从第一行排列进而完全推出第二行,然后第三行。。。) 思路:枚举第一行的状态(用01枚举子集)时间复杂度 O ( 2 n ) O(2^n) O ( 2 n ) 然后计算2~n行状态,这样总的时间复杂度为: O ( n 2 ∗ 2 n ) O(n^2*2^n) O ( n 2 ∗ 2 n ) code: # include <bits/stdc++.h> using namespace std ; const int N = 20 ; const int inf = 0x3f3f3f ; int n , a [ N ] [ N ] , b [ N ] [ N ] ; int check ( int s ) { memset ( b , 0 , sizeof ( b ) ) ; for ( int c = 0 ; c < n ; c ++ ) { if ( s & ( 1 << c ) ) b [ 0 ] [ c ] = 1 ; else if ( a [ 0 ] [ c ] == 1 ) return inf ; } for ( int r = 0 ; r < n - 1 ; r ++ ) for ( int c = 0 ; c < n ; c

浅谈二分算法

我的梦境 提交于 2020-01-25 22:08:57
二分算法 程序或算法的时间复杂度 基本概念 一个程序或算法的时间效率,也称“时间复杂度”,有时简称“复杂度” 复杂度常用大写字母 O O O 和小写字母 n n n 来表示, n n n 代表问题的规模 时间复杂度是用算法运行过程中,某种时间固定的操作需要被执行的次数和 n n n 的关系来衡量的。在无序数列中查找某个数,复杂度是 O ( n ) O(n) O ( n ) 。 计算复杂度的时候,只统计执行次数最多的( n n n 足够大时)那种固定操作的次数。比如某个算法需要执行加法 n 2 n^2 n 2 次,除法 n n n 次,那么就记其复杂度是 O ( n 2 ) O(n^2) O ( n 2 ) 的。 复杂度有“平均复杂度”和“最坏复杂度”两种。两者可能一致,也可能不一致。一般情况下,只需考虑“平均复杂度”,只有在要求极为严格的情况下,才需要考虑“最坏复杂度” 如果复杂度是多个 n n n 的函数之和,则只关心随 n n n 增长增长得最快的那个函数 O ( n 2 + n 3 ) = > O ( n 3 ) O(n^2+n^3)=>O(n^3) O ( n 2 + n 3 ) = > O ( n 3 ) O ( 2 n + n 2 ) = > O ( 2 n ) O(2^n+n^2)=>O(2^n) O ( 2 n + n 2 ) = > O ( 2 n ) O ( n

算法运行时间复杂度

試著忘記壹切 提交于 2020-01-25 18:23:57
算法的运行时间复杂度分析,一般是求输入规模作为自变量,运行时间作为因变量的函数。并不是求所有语句执行的真实代价,是考虑算法运行时间的增长率(增长的量级),只求出公式中的最高次项,忽略低次项和系数。 经常的情况是,输入规模相同,但某种输入会使算法的运行时间其他输入更长。所以算法的时间复杂度可能会有个定语修饰。 最坏情况下:某种输入下,运行时间最长的情况 平均情况下:概率分布分析,算法时间复杂度的期望 最好情况下:某种输入下,运行时间最短的情况 时间复杂度分析用到的渐进记号 O:算法运行的渐近上界 当得到算法在最坏情况下运行时间上界为O(f(n)),可以说算法在任何情况下运行时间上界是O(f(n)). 因此特性,多数时候要求找到算法的渐近上界。 Ω:算法运行的渐近下界 当得到算法在最好情况下运行时间下界为 Ω(f(n)),可以说算法在任何情况下运行时间下界是 Ω(f(n)). θ:算法运行的确界 算法运行时间T(n)=θ(f(n))当且仅当算法时间复杂度T(n)=O(f(n))且T(n)=Ω(f(n)) o记号:非渐近紧确的上界 比如:T(n)=2n,有T(n)=O(n).那么n^2是T(n)的上界,但不是紧确的上界,有T(n)=o(n^2) ω记号:非渐近紧确的下界,ω和Ω的关系如o和O的关系 几种常见的算法运行时间渐近上界,根据函数性质可知运行时间对比(输入规模足够大): O(1)

时间复杂度

血红的双手。 提交于 2020-01-25 06:10:31
算法效率衡量 执行时间反应算法效率 对于同一问题,我们给出了两种解决算法,在两种算法的实现中,我们对程序执行的时间进行了测算,发现两段程序执行的时间相差悬殊(214.583347秒相比于0.182897秒),由此我们可以得出结论:实现算法程序的执行时间可以反应出算法的效率,即算法的优劣。 单靠时间值绝对可信吗? 假设我们将第二次尝试的算法程序运行在一台配置古老性能低下的计算机中,情况会如何?很可能运行的时间并不会比在我们的电脑中运行算法一的214.583347秒快多少。 单纯依靠运行的时间来比较算法的优劣并不一定是客观准确的! 程序的运行离不开计算机环境(包括硬件和操作系统),这些客观原因会影响程序运行的速度并反应在程序的执行时间上。那么如何才能客观的评判一个算法的优劣呢? 时间复杂度与“大O记法” 我们假定计算机执行算法每一个基本操作的时间是固定的一个时间单位,那么有多少个基本操作就代表会花费多少时间单位。算然对于不同的机器环境而言,确切的单位时间是不同的,但是对于算法进行多少个基本操作(即花费多少时间单位)在规模数量级上却是相同的,由此可以忽略机器环境的影响而客观的反应算法的时间效率。 对于算法的时间效率,我们可以用“大O记法”来表示。 “大O记法”: 对于单调的整数函数f,如果存在一个整数函数g和实常数c>0,使得对于充分大的n总有f(n)<=c*g(n)

数据结构之链表

雨燕双飞 提交于 2020-01-25 04:46:34
数据需要一块连续的内存空间来存储,对内存的要求比较高。而链表恰恰相反,它并不需要一块连续的内存空间,它通过“ 指针 ”将一组 零散 的内存块串联起来使用。 最常见的链表:单链表、双链表、和循环链表。 单链表 结点 :除了存储数据之外,还要记录链上的下一个节点的地址,如下图,我们把这个记录下个结点地址的指针叫作 后继指针 next 头结点 :用来记录链表的基地址。 尾结点 :指向一个空地址 NULL 针对链表的插入和删除操作,只需考虑相邻结点的指针改变,所以对应的时间复杂度是O(1)。 链表想要随机访问第k个元素,就没有数组那么高效,链表的数据并非连续存储的,所以无法像数组那样,根据首地址和下标通过寻址公式就能直接计算出对应的内存地址,而是要根据指针一个结点一个结点的一次遍历,直到找到相应的结点。需要 O(n)时间复杂度 。 循环链表 是一种特殊的单链表。它和单链表唯一的区别就在尾结点。循环链表的 尾结点指针是指向链表的头结点 。 当处理的数据具有环形结构特点时,就特别适合采用循环链表。 双向链表 双向链表支持双向遍历,它需要额外的两个空间来存储后继结点和前驱结点的地址。 时空替换思想 :“用空间换时间” 与 “用时间换空间” 当内存空间充足的时候,如果我们更加追求代码的执行速度,我们就可以选择空间复杂度相对较高,时间复杂度小相对较低的算法和数据结构,缓存就是空间换时间的例子

经典算法(三) 单链表 反转 & 是否相交/成环 & 求交点 等

徘徊边缘 提交于 2020-01-23 07:21:39
参考文章: 判断链表是否相交: http://treemanfm.iteye.com/blog/2044196 一、单链表反转 链表节点 public class Node { private int record; private Node nextNode; public Node(int record) { super(); this.record = record; } } View Code 构建链表 public static Node creatLinkedList() { Node head = new Node(0); Node tmp = null; Node cur = null; for (int i = 1; i < 10; i++) { tmp = new Node(i); if (1 == i) { head.setNextNode(tmp); } else { cur.setNextNode(tmp); } cur = tmp; } return head; } View Code 递归实现 public static Node reverse(Node head) { if (null == head || null == head.getNextNode()) { return head; } Node reversedHead =

python进阶-算法

谁都会走 提交于 2020-01-22 20:16:46
算法:解决问题的方法和步骤 评价算法的好坏:渐近时间复杂度和渐近空间复杂度。 渐近时间复杂度的大O标记: - 常量时间复杂度 - 布隆过滤器 / 哈希存储 - 对数时间复杂度 - 折半查找(二分查找) - 线性时间复杂度 - 顺序查找 / 桶排序 - 对数线性时间复杂度 - 高级排序算法(归并排序、快速排序) - 平方时间复杂度 - 简单排序算法(选择排序、插入排序、冒泡排序) - 立方时间复杂度 - Floyd算法 / 矩阵乘法运算 - 几何级数时间复杂度 - 汉诺塔 - 阶乘时间复杂度 - 旅行经销商问题 - NP 排序算法(选择、冒泡和归并)和查找算法(顺序和折半) 选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。 函数: def select_sort(origin_items, comp=lambda x, y: x < y): """简单选择排序""" items = origin_items[:] for i in range(len(items) - 1): min_index = i for j in range(i + 1, len(items)): if