时间复杂度

链表

元气小坏坏 提交于 2020-03-08 21:38:39
一.常见的缓存淘汰策略: 1.先进先出策略FIFO 2.最少使用策略LFU 3.最近最少使用策略LRU 二.链表 (一)链表的定义:链表是物理存储单元上 非连续 的、 非顺序 的存储结构,它由一个个结点,通过指针联系起来的,每个结点包括数据和指针。 (二)链表结构: 1.单链表 通过“指针”将一组零散的内存块串联起来使用。内存块称为结点;记录下个结点地址的指针称为后继指针next;第一个结点为头结点,用来记录链表的基地址;最后一个结点为尾结点,指针指向NULL。 链表的插入、删除操作,只需考虑相邻结点的指针变化,不需要数据搬移,时间复杂度为 O(1) 。 随机访问的时间复杂度为O(n) 。 2.循环链表 是一种特殊的单链表。尾结点的指针指向链表的头结点。 从链尾到链头比较方便。当要处理的数据具有环形结构特点时,适合采用循环链表。例如约瑟夫问题。 3.双向链表 每个结点有两个指针,分别为后继指针next和前驱指针prev。找到前驱结点的时间复杂度为O(1)。 4.双向循环链表 三. 1.删除操作 (1)删除结点中“值等于某个给定值”的结点 从头结点开始一个一个遍历,直到找到值等于给定值的结点,再删除。删除操作时间复杂度为O(1),查找操作时间复杂度为O(n);总时间复杂度为O(n)。 (2)删除给定指针指向的结点 对于单链表来说,要从头结点开始遍历找到给定结点的前驱结点,再删除

数据结构与算法系列十(排序算法概述)

匆匆过客 提交于 2020-03-08 13:43:47
1.引子 1.1.为什么要学习数据结构与算法? 有人说,数据结构与算法,计算机网络,与操作系统都一样,脱离日常开发,除了面试这辈子可能都用不到呀! 有人说,我是做业务开发的,只要熟练API,熟练框架,熟练各种中间件,写的代码不也能“飞”起来吗? 于是问题来了:为什么还要学习数据结构与算法呢? #理由一: 面试的时候,千万不要被数据结构与算法拖了后腿 #理由二: 你真的愿意做一辈子CRUD Boy吗 #理由三: 不想写出开源框架,中间件的工程师,不是好厨子 1.2.如何系统化学习数据结构与算法? 我想好了,还是需要学习数据结构与算法。但是我有两个困惑: 1.如何着手学习呢? 2.有哪些内容要学习呢? 学习方法推荐: #学习方法 1.从基础开始,系统化学习 2.多动手,每一种数据结构与算法,都自己用代码实现出来 3.思路更重要:理解实现思想,不要背代码 4.与日常开发结合,对应应用场景 学习内容推荐: 数据结构与算法内容比较多,我们本着实用原则,学习经典的、常用的数据结构、与常用算法 #学习内容: 1.数据结构的定义 2.算法的定义 3.复杂度分析 4.常用数据结构 数组、链表、栈、队列 散列表、二叉树、堆 跳表、图 5.常用算法 递归、排序、二分查找 搜索、哈希、贪心、分治 动态规划、字符串匹配 2.考考你 在前面两篇,我们详细看了常用算法的第一个主题:递归

[LeetCode] 347. Top K Frequent Elements

你说的曾经没有我的故事 提交于 2020-03-08 04:43:00
前K个高频元素。题意是给一个非空数组,请返回前K个高频元素。 题目要求时间复杂度必须优于O(nlogn) 。例子, Example 1: Input: nums = [1,1,1,2,2,3], k = 2 Output: [1,2] Example 2: Input: nums = [1], k = 1 Output: [1] 因为题目有时间复杂度的要求所以只能是bucket sort桶排序的思想做。这题Java的时间复杂度稍稍优于JS,是因为实现方式的不同导致。 Java时间O(n),JS时间O(logn) 空间O(n) 解释一下思路,既然是桶排序,所以需要将出现频率一样的元素放到同一个桶里面,所以这里会用到一个hashmap记录 数字 和他们各自 出现频率 (key:value)。接着就需要根据频率大小,挑出前K个频率最大的元素。在JS里面,map是可以根据value排序的(21行),排序完之后,可以根据排序结果,再反过来挑出前K个key(24行)。因为21行用到了快排quick sort,所以时间复杂度至少为O(logn)。 1 /** 2 * @param {number[]} nums 3 * @param {number} k 4 * @return {number[]} 5 */ 6 var topKFrequent = function (nums, k) { 7

数据结构-数组

你说的曾经没有我的故事 提交于 2020-03-07 17:46:50
  一、数组的概念   定义:数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据。   从概念中可以知道一下几点: 数组是线性表。     所谓的线性表就是数据排成一排,想一条线一样的结构。每个线性表上的数据最多只有前和后两个方向。当然除了数组,链表、队列、栈等也是线性表结构        连续的内存空间和形同类型的数据。    正因为有了上述两个特点,数组才能够有一个堪称“杀手锏”的特性:随机访问 数组实现下标随机访问   下面通过一个实际的例子来说明:   例如有一个长度为10的int数组,int[] a = new int[10].      计算机给数组a[10]分配了一块连续的内存空间1000~1039,其中,内存块的首地址为base_address = 1000.   计算机会为每个内存单元分配一个地址,计算机通过地址来访问内存中的数据。当计算即需要随机访问数组中的某个元素的时候,它会首先通过下面的寻址公式,计算该元素存存储的内存地址:            a[i]_address = base_address + i * data_type_size    其中data_type_size表示数组中每个元素的大小。例如,数组中存储的int类型的数据,所以,data_type_size就是4字节。 二、数组的操作  

求解递归式时间复杂度

梦想的初衷 提交于 2020-03-07 00:06:43
方法一:代换法(代入法) 这种方法分为两个步骤: 1、 猜答案 ,我们只需要猜一下大致的形式 比如 T(n)= 4T(n/2) +n 通过观察该递归式,注意到当n加倍时,输出增加4倍,于是猜测该递归式时间复杂度为O(n 2 ),即T(n) = O(n 2 ) 。 2、 数学归纳法证明 我们用带常数系数的展开 T(k) ≤ c 1 k 2 -c 2 k(k<n) T(n) ≤4[c 1 (n/2) 2 -c 2 (n/2)]+n=c 1 n 2 +(1-2c 2 )n = c 1 n 2 -c 2 n-(c 2 -1)n≤c 1 n^2-c 2 n 这里我们就得到了时间复杂度O(n 2 ) 那么我们为什么不用ck 2 呢,看上式看不出来的话,可以推导一下,化简为 cn 2 +n,这个式子我们得不到T(n) ≤cn 2 ,所以通过降阶来实现(算时间复杂度我们只考虑高阶,不用管低阶) 方法二:递归树法(迭代法) 这个应该式比较好理解的了吧,就是一项一项展开,项数以等比数列方式增长(d k-1 ,每次可以递归d次,k为递归的层数),这里的话就可以理解成 满d叉树 。 来源: CSDN 作者: 萌小兔~ 链接: https://blog.csdn.net/weixin_44417475/article/details/104703670

跨越算法开篇

混江龙づ霸主 提交于 2020-03-06 23:39:09
关注公众号 MageByte,设置星标获取最新推送。公众号后台回复 “加群” 进入技术交流群获更多技术成长。 数据结构与算法是编程的基本功,当你算法掌握越来越深的时候你会发现写代码的时候,会不由自主考虑很多性能方面的问题。写出时间复杂度高、空间复杂度高的垃圾代码越来越少了,算法能力提升了很多,编程能力也有了质的飞跃。 首先一个三连问。 是不是从学校开始,你就觉得数据结构难学,然后一直没认真学? 工作中,一遇到数据结构这个坑,你又发自本能地迅速避让,因为你觉得自己不懂,所以也不想深究,反正看起来无关大局? 当你想换工作面试,或者研究某个开源项目源码,亦或者和团队讨论某个非框架层面的高可用难题的时候,你又发现,自己的基础跟不上别人的节奏? 如果你有这种情况,其实你并不孤独。这不是你一个人遇到的问题。工作十间,见过许多程序员。他们有着各种各样的背景,有很多既有潜力又非常努力,但始终无法在自己现有水平上更进一步。少年不要慌,青叶带你跨越算法这道坎。 在技术圈里,我们经常喜欢谈论高大上的架构,比如高可用、微服务、服务治理等等。鲜有人关注代码层面的编程能力,而愿意沉下心来,花几个月时间啃一啃计算机基础知识、认认真真夯实基础的人,简直就是凤毛麟角。 基础知识就像是一座大楼的地基,它决定了我们的技术高度。而要想快速做出点事情,前提条件一定是基础能力过硬,“内功”要到位 。

大数据面试之——大数据解决方案思维题

丶灬走出姿态 提交于 2020-03-06 17:09:11
1.给定a、b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,让你找出a、b文件共同的url? 方案1:假如每个url大小为10bytes,那么可以估计每个文件的大小为50G×64=320G,远远大于内存限制的4G,所以不可能将其完全加载到内存中处理,可以采用分治的思想来解决。 Step1:遍历文件a,对每个url求取hash(url)%1000,然后根据所取得的值将url分别存储到1000个小文件(记为a0,a1,…,a999,每个小文件约300M); Step2:遍历文件b,采取和a相同的方式将url分别存储到1000个小文件(记为b0,b1,…,b999); 巧妙之处:这样处理后,所有可能相同的url都被保存在对应的小文件(a0vsb0,a1vsb1,…,a999vsb999)中,不对应的小文件不可能有相同的url。然后我们只要求出这个1000对小文件中相同的url即可。 Step3:求每对小文件ai和bi中相同的url时,可以把ai的url存储到hash_set/hash_map中。然后遍历bi的每个url,看其是否在刚才构建的hash_set中,如果是,那么就是共同的url,存到文件里面就可以了。 方案2:如果允许有一定的错误率,可以使用Bloomfilter,4G内存大概可以表示340亿bit

交换排序

荒凉一梦 提交于 2020-03-06 12:30:14
1 交换排序基本思想 交换排序的基本思想是:两两比较待排序记录的关键字,发现两个记录的次序相反时即进行交换,直到没有反序的记录为止。 应用交换排序基本思想的主要排序方法有:冒泡排序(Bubble sort)和快速排序(Quick sort)。 2 冒泡排序 2.1 冒泡排序思想 第一趟排序:首先将第一个记录的关键字和第二个记录的关键字比较,若为逆序,则将两个记录交换之,然后比较第二个和第三个的关键字。以此类推,直至第n-1个记录和第n个记录关键字比较为止。该过程为第一趟排序,使得最大的关键字排到了最后面。第二趟排序:对前n-1个记录进行相同操作,完成后使得次大的关键字排在n-1位置上。以此类推,进行第三、四次排序直到排序结束。判断排序结束条件是:在一趟排序过程中没有发生过交换记录的操作。一般第i趟排序是从第1个元素到(n-i+1)个记录依次比较相邻两个记录关键字,并在逆序时交换记录。 总结便是:共比较多少趟,每趟比较多少次。一个两重循环。 2.2 冒泡排序算法实现 public static void bubbleSort(int a[]) { int lenth = a.length; int temp; //共比较lenth-1趟 for (int i = 0; i < lenth - 1; i++) { //每轮比较length-1-j次 for (int j = 0; j

跨越算法开篇

对着背影说爱祢 提交于 2020-03-06 00:25:01
数据结构与算法是编程的基本功,当你算法掌握越来越深的时候你会发现写代码的时候,会不由自主考虑很多性能方面的问题。写出时间复杂度高、空间复杂度高的垃圾代码越来越少了,算法能力提升了很多,编程能力也有了质的飞跃。 首先一个三连问。 是不是从学校开始,你就觉得数据结构难学,然后一直没认真学? 工作中,一遇到数据结构这个坑,你又发自本能地迅速避让,因为你觉得自己不懂,所以也不想深究,反正看起来无关大局? 当你想换工作面试,或者研究某个开源项目源码,亦或者和团队讨论某个非框架层面的高可用难题的时候,你又发现,自己的基础跟不上别人的节奏? 如果你有这种情况,其实你并不孤独。这不是你一个人遇到的问题。工作十间,见过许多程序员。他们有着各种各样的背景,有很多既有潜力又非常努力,但始终无法在自己现有水平上更进一步。少年不要慌,青叶带你跨越算法这道坎。 在技术圈里,我们经常喜欢谈论高大上的架构,比如高可用、微服务、服务治理等等。鲜有人关注代码层面的编程能力,而愿意沉下心来,花几个月时间啃一啃计算机基础知识、认认真真夯实基础的人,简直就是凤毛麟角。 基础知识就像是一座大楼的地基,它决定了我们的技术高度。而要想快速做出点事情,前提条件一定是基础能力过硬,“内功”要到位 。 那技术人究竟都需要修炼哪些“内功”呢?我觉得,无外乎就是大学里的那些基础课程,操作系统、计算机网络、编译原理等等

数据库 redis底层实现

这一生的挚爱 提交于 2020-03-05 22:51:12
redis是一个存储键值对的内存数据库,并且持久化到磁盘。 1、简单动态字符串(Simple Dynamic String,简称SDS) 1)利用len记录字符串长度,使得strlen时间复杂度从O(N)变为O(1)。 // sds.h struct __attribute__ ((__packed__)) sdshdr64 { uint64_t len; // 字符串已用的长度 uint64_t alloc; // 分配的总长度 char buf[]; // 字符串内容 }; 2)利用类似vector的分配策略,append时预分配一倍空间,减少内存分配次数。 // sds.c/sdsMakeRoomFor if (avail >= addlen) return s; newlen = (len+addlen); if (newlen < SDS_MAX_PREALLOC) newlen *= 2; else newlen += SDS_MAX_PREALLOC; sdssetalloc(s, newlen); 2、双向链表(Doubly Linked List) 1)利用len,使得计算链表长度的时间复杂度从O(N)变为O(1)。 2)表头和表尾插入元素的时间复杂度都是O(1)。注:这是所有双向链表的特性。 // adlist.h typedef struct listNode