时间复杂度

数据结构常见面试题

让人想犯罪 __ 提交于 2019-12-15 20:44:56
数据结构常见面试题 1、数组和链表的区别。 从逻辑结构上来看 ,数组必须实现定于固定的长度,不能适应数据动态增减的情况,即数组的大小一旦定义就不能改变。当数据增加是,可能超过原先定义的元素的个数;当数据减少时,造成内存浪费;链表动态进行存储分配,可以适应数据动态地增减的情况,且可以方便地插入、删除数据项。 从内存存储的角度看 ;数组从栈中分配空间(用new则在堆上创建),对程序员方便快速,但是自由度小;链表从堆中分配空间,自由度大但是申请管理比较麻烦。 从访问方式类看 ,数组在内存中是连续的存储,因此可以利用下标索引进行访问;链表是链式存储结构,在访问元素时候只能够通过线性方式由前到后顺序的访问,所以访问效率比数组要低。 2、简述快速排序过程 1)选择一个基准元素,通常选择第一个元素或者最后一个元素, 2)通过一趟排序将待排序的记录分割成独立的两部分,其中一部分记录的元素值均比基准元素值小。另一部分记录的元素值比基准值大。 3)此时基准元素在其排好序后的正确位置 4)然后分别对这两部分记录用同样的方法继续进行排序,直到整个序列有序。 3、快速排序的改进 只对长度大于k的子序列递归调用快速排序,让原序列基本有序,然后再对整个基本有序序列用插入排序算法排序。实践证明,改进后的算法时间复杂度有所降低,且当k取值为 8 左右时,改进算法的性能最佳。 选择基准元的方式 对于分治算法

【Leetcode 做题学算法周刊】第六期

好久不见. 提交于 2019-12-15 17:27:17
首发于微信公众号《前端成长记》,写于 2019.12.15 背景 本文记录刷题过程中的整个思考过程,以供参考。主要内容涵盖: 题目分析设想 编写代码验证 查阅他人解法 思考总结 目录 110.平衡二叉树 111.二叉树的最小深度 112.路径总和 118.杨辉三角 119.杨辉三角Ⅱ Easy 110.平衡二叉树 题目地址 题目描述 给定一个二叉树,判断它是否是高度平衡的二叉树。 本题中,一棵高度平衡二叉树定义为: 一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。 示例 1: 给定二叉树 [3,9,20,null,null,15,7] 3 / \ 9 20 / \ 15 7 返回 true 。 示例 2: 给定二叉树 [1,2,2,3,3,null,null,4,4] 1 / \ 2 2 / \ 3 3 / \ 4 4 返回 false 。 题目分析设想 我们上一期做过通过遍历求二叉树的最大深度的题目,这题最粗暴的一个方案就是计算出每个子树的最大深度做高度判断,很明显,这个效率低下。我们可以通过改成自底而上的方案,当中间过程不符合,则可以跳出计算。 编写代码验证 Ⅰ.计算子树最大深度做判断 代码: /** * @param {TreeNode} root * @return {boolean} */ var isBalanced = function(root) {

452. 用最少数量的箭引爆气球

好久不见. 提交于 2019-12-15 09:55:12
ID: 452 TITLE: 用最少数量的箭引爆气球 TAG: Java,Python,C++,贪心算法 方法:贪心算法 贪心算法一般用来解决需要 “找到要做某事的最小数量” 或 “找到在某些情况下适合的最大物品数量” 的问题,且提供的是无序的输入。 贪心算法的思想是每一步都选择最佳解决方案,最终获得全局最佳的解决方案。 标准解决方案具有 O ( N log ⁡ N ) \mathcal{O}(N \log N) O ( N lo g N ) 的时间复杂度且由以下两部分组成: 思考如何排序输入数据( O ( N log ⁡ N ) \mathcal{O}(N \log N) O ( N lo g N ) 的时间复杂度)。 思考如何解析排序后的数据( O ( N ) \mathcal{O}(N) O ( N ) 的时间复杂度) 如果输入数据本身有序,则我们不需要进行排序,那么该贪心算法具有 O ( N ) \mathcal{O}(N) O ( N ) 的时间复杂度。 如何证明你的贪心思想具有全局最优的效果:可以使用反证法来证明。 让我们来看下面的气球箭的组合情况。 很明显我们使用两支箭就能使全部气球引爆,我们借助此例子来思考如何用贪心算法的思想来计算结果。 让我们根据气球的结束坐标进行排序,然后一个个进行检查。第一个气球是标有 0 的绿色气球,它的结束坐标是 6 。

【算法笔记】排序优化

喜欢而已 提交于 2019-12-15 06:19:16
排序优化 1.如何选择合适的排序算法 2.如何用优化快速排序 1. 三数取中法 2. 随机法 3.分析qsort() 函数的底层实现原理 1.如何选择合适的排序算法 如果对小规模数据进行排序,可以选择时间复杂度是 O(n2) 的算法;如果对大规模数据进行排序,时间复杂度是 O(nlogn) 的算法更加高效。所以,为了兼顾任意规模数据的排序,一般都会首选时间复杂度是 O(nlogn) 的排序算法来实现排序函数。 快速排序比较适合来实现排序函数,但是,我们也知道,快速排序在最坏情况下的时间复杂度是 O(n2) 2.如何用优化快速排序 最理想的分区点是:被分区点分开的两个分区中,数据的数量差不多; 1. 三数取中法 从区间的首、尾、中间,分别取出一个数,然后对比大小,取这 3 个数的中间值作为分区点。这样每间隔某个固定的长度,取数据出来比较,将中间值作为分区点的分区算法,肯定要比单纯取某一个数据更好。但是,如果要排序的数组比较大,那“三数取中”可能就不够了,可能要“五数取中”或者“十数取中”。 2. 随机法 随机法就是每次从要排序的区间中,随机选择一个元素作为分区点。这种方法并不能保证每次分区点都选的比较好,但是从概率的角度来看,也不大可能会出现每次分区点都选的很差的情况,所以平均情况下,这样选的分区点是比较好的。时间复杂度退化为最糟糕的 O(n2) 的情况,出现的可能性不大。 3

链表为什么插入比数组快

心已入冬 提交于 2019-12-15 01:48:34
链表和数组是两种截然不同的内存组织方式,正因如此,它们插入、删除、随机访问的时间复杂度正好相反。 数组使用的是连续的内存空间,可以利用空间局部性原理,借助 CPU cache进行预读,所以访问效率更高。而链表不是连续存储,无法进行缓存,随机访问效率也较低。 数组的缺点是大小固定,一经声明就要占用整块连续的内存空间。如果声明的数组过大,系统可能没有足够的连续内存空间用于分配,就会导致“内存不足(out of memory)”。而如果声明的数组过小,当不够用时,又需要重新申请一块更大的内存,然后进行数据拷贝,非常费时。 而链表则没有大小限制,支持动态扩容。当然,因为链表中每个结点都需要存储前驱 / 后继结点的指针,所以内存消耗会翻倍。而且,对链表频繁的插入、删除操作会导致频繁的内存申请和释放,容易造成内存碎片和触发垃圾回收(Garbage Collection, GC) 数组和链表插入删除操作的时间复杂度对比: 在只知道下标时 数组:O(n) 需要移动其后所有项的位置 链表:O(n) 需要遍历其前面所有项以得到下标对应的项 在拥有要操作的项的引用时 数组:O(n) 需要移动其后所有项的位置 链表:O(1) 无需遍历 其实直接就插入删除的执行函数来看的话,链表和数组在只知道下标的情况下,其时间复杂度都是O(n),性能上是不会有太大差别的。因为数组不需要遍历,能直接取得要操作的对象

数据结构(树)

心已入冬 提交于 2019-12-14 17:39:14
【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>> 树: 树是 n个结点 的有限集合,有且仅有一个 根结点, 其余结点可分为m个根结点的 子树。 度: 指的是一个节点拥有子节点的个数。如二叉树的节点的最大度为2。 高度/深度: 数的层数,根节点为第一层,依次类推。 叶子节点: 度为0的节点,即没有子节点的节点。 二叉树: 在二叉树中每个节点最多有两个子节点,一般称为左子节点和右子节点(或左孩子和右孩子) 前序遍历(前根遍历): 根 ——>左——>右 中序遍历(中根遍历):左——> 根 ——>右 后序遍历(后根遍历):左——>右——> 根 满二叉树: 在一棵二叉树中,如果所有分支结点都有左孩子和右孩子结点,并且叶子结点都集中在二叉树的最下层,这样的树叫做满二叉树 高度为h,由2^h-1个节点构成的二叉树。 完全二叉树: 二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数(即1~h-1层为一个满二叉树),第 h 层所有的结点都连续集中在最左边。 二叉查找树(BST): 又称二叉排序树,亦称二叉搜索树(Binary Search Tree)。 定义: 一棵空树,或者是具有下列性质的二叉树: 1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值; 2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值; 3)左

如何实现LRU缓存淘汰算法(极客时间)

。_饼干妹妹 提交于 2019-12-14 01:32:33
1.如何分别用链表和数组实现LRU缓冲淘汰策略? 1)什么是缓存? 缓存是一种提高数据读取性能的技术,在硬件设计、软件开发中都有着非广泛的应用,比如常见的CPU缓存、数据库缓存、浏览器缓存等等。 2)为什么使用缓存?即缓存的特点 缓存的大小是有限的,当缓存被用满时,哪些数据应该被清理出去,哪些数据应该被保留?就需要用到缓存淘汰策略。 3)什么是缓存淘汰策略? 指的是当缓存被用满时清理数据的优先顺序。 4)有哪些缓存淘汰策略? 常见的3种包括先进先出策略FIFO(First In,First Out)、最少使用策略LFU(Least Frenquently Used)、最近最少使用策略LRU(Least Recently Used)。 5)链表实现LRU缓存淘汰策略 当访问的数据没有存储在缓存的链表中时,直接将数据插入链表表头,时间复杂度为O(1);当访问的数据存在于存储的链表中时,将该数据对应的节点,插入到链表表头,时间复杂度为O(n)。如果缓存被占满,则从链表尾部的数据开始清理,时间复杂度为O(1)。 6)数组实现LRU缓存淘汰策略 方式一 :首位置保存最新访问数据,末尾位置优先清理 当访问的数据未存在于缓存的数组中时,直接将数据插入数组第一个元素位置,此时数组所有元素需要向后移动1个位置,时间复杂度为O(n);当访问的数据存在于缓存的数组中时

算法性能分析

爷,独闯天下 提交于 2019-12-13 21:54:54
性能分析的角度 一般我们从 时间复杂度 和 空间复杂度 两个方面对算法的效率进行分析。 一.时间复杂度(Time Complexity) 我们通常将时间复杂度记为: T ( n ) = O ( f ( n ) ) 我们将此式分为三个部分进行讲述。 T(n) :算法的渐近时间复杂度,简称时间复杂度。 f(n) :算法中语句中执行次数最多的语句的频度。 O() :计算数量级。 数学表达式为: 存在正常数C和n 0 ,当n>=n 0 时,0<=T(n)<=C*f(n) 1.三种时间复杂度 最坏时间复杂度:最坏情况下的时间复杂度。 平均时间复杂度:所有输入实例等概率出现情况。 最好时间复杂度:最好情况下时间复杂度。 我们利用一个例子说明: 在冒泡排序中,如果序列本有序,我们只需比较 n-1 次,排序完成,则为最好情况下时间复杂度为 O(n)。如果序列为逆序,则需要比较 (n-1)+(n-2)+....+1=n*(n-1)/2 ,此时时间复杂度为 O(n²)。则平均复杂的也为O(n²)。 2.常见的时间复杂度(从小到大排列) 常数阶:O(1) 对数阶:O(log 2 n) void fun ( int n ) { int i = 1 ; while ( i <= n ) i = i * 2 ; } 线性阶:O(n) for ( int i = 0 ; i ++ ; i < n ) 线性对数阶

一套图 搞懂“时间复杂度”

本小妞迷上赌 提交于 2019-12-13 20:05:01
写在前面: 这篇文章是在公众号: 程序员小灰 中发布的。是我到目前为止所看到的关于时间复杂度介绍的最好的文章,简介 清晰 明了。 所以拿来po出来 仅供学习交流,如侵则删。 现已将此文收录至: 《数据结构》C语言版 (清华严蔚敏考研版) 全书知识梳理 正文: 时间复杂度的意义 究竟什么是时间复杂度呢?让我们来想象一个场景:某一天,小灰和大黄同时加入了一个公司...... 一天过后,小灰和大黄各自交付了代码,两端代码实现的功能都差不多。大黄的代码运行一次要花100毫秒,内存占用5MB。小灰的代码运行一次要花100秒,内存占用500MB。于是...... 由此可见,衡量代码的好坏,包括两个非常重要的指标: 1.运行时间; 2.占用空间。 基本操作执行次数 关于代码的基本操作执行次数,我们用四个生活中的场景,来做一下比喻: 场景1: 给小灰一条长10寸的面包,小灰每3天吃掉1寸,那么吃掉整个面包需要几天? 答案自然是 3 X 10 = 30天。 如果面包的长度是 N 寸呢? 此时吃掉整个面包,需要 3 X n = 3n 天。 如果用一个函数来表达这个相对时间,可以记作 T(n) = 3n。 场景2: 给小灰一条长16寸的面包,小灰每5天吃掉面包剩余长度的一半,第一次吃掉8寸,第二次吃掉4寸,第三次吃掉2寸......那么小灰把面包吃得只剩下1寸,需要多少天呢? 这个问题翻译一下

六种常见排序算法(Python语言实现)

半世苍凉 提交于 2019-12-12 23:28:37
文章目录 排序算法总结 常见算法时间复杂度比较 冒泡排序 简介与工作原理: 代码实现 选择排序 插入排序 快速排序 希尔排序 归并排序 排序算法总结 常见算法时间复杂度比较 冒泡排序 简介与工作原理: 简介:是一种简单的排序算法。它重复地遍历要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。遍历数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。 从第一个元素开始,与相邻的元素进行比较,如果比较结果是升序的,就把这两个元素进行互换 从第一对到最后一对进行比较,对每一对相邻元素重复进行此操作,做完这个操作后,最后一个元素是最大的元素 针对所以的元素再重复以上步骤 持续对越来越少的元素进行以上的步骤,直到没有任何一对数据需要比较为止。 代码实现 #!/usr/local/bin/python 2 # 冒泡排序 3 def bubb_sort ( list ) : 4 L = len ( list ) 5 for j in range ( L , 0 , - 1 ) : 6 for i in range ( j - 1 ) : 7 if list [ i ] > list [ i + 1 ] : 8 list [ i ] , list [ i + 1 ] = list [ i + 1