上两篇: 算法(1) 算法(2) 一、常见的时间复杂度 常用的时间复杂度.png 二、最坏情况和平均情况 最坏情况运行时间是一种保证,那就是运行时间将不会再坏了 平均时间是所有情况中最有意义的 对算法的分析,一种方法是计算所有情况的平均值,这种时间复杂度的计算方法称为时间复杂度。另一种方法是计算最坏情况下的时间复杂度,这种方法称为最坏时间时间复杂度。 三、算法空间复杂度 算法的空间复杂度通过计算算法所需的存储空间实现,算法空间复杂度的计算公式记作:S(n) = O(f(n)),其中,n为问题的规模,f(n)为语句关于n所占存储空间的函数. 结尾语: 很多学生,学了四年计算机专业,很多程序员,做了很长时间的编程工作,却始终都弄不明白算法的时间复杂度的估算,这是很可悲的一件事。 因为弄不清楚,所以也就从不深究自己的代码是否效率低下,是不是可以通过优化让计算机更加快速高效。 算法的重要
1 配置pom文件 # 雪花算法配置数据中心和机器编号,不同机器组合不能重复 snowflake: datacenterId: 1 machineId: 2 2 编写配置文件 SnowFlakeFactory.java ,但是可部署的sequence服务越少, * 设置BACKUP_COUNT为3,最多可以部署1024/(3+1)即256个sequence服务,完全够用, * 抗时钟回拨影响的能力也得到非常大的保障 IllegalStateException("时钟在向后移动,当前时间是 " + currentMillis + " 毫秒,machineId映射 = " + machineIdLastTimeMap); } } 定义一个枚举 ; } public void setMachineId(long machineId) { this.machineId = machineId; } } 3 SnowFlakeController { @Autowired private SnowFlakeFactory snowFlakeFactory; /** * 雪花算法测试
一:仅仅反转字母 思路一: 1:第一次遍历s把非字母扔进数组中 2:第二次遍历s,把字母进栈 3:出栈填充数组 麻烦!~! 二叉树的直径 心得:在于将问题进行转化 1:二叉树的直径 = 任意两节点间最短路径的最大值 = 根节点左(边的数目的最大值)+ 右(边的数目的最大值) 2:两节点最短路径 = 它们之间边的数目 = 左(边的数目) + 右 (边的数目) 3:边的数目 这样我们就把求直径转化为了求左右边的数目之和 明显 3,4,5,边的数目为0 ,2的边的数目= 1 , 1的边的数目 = 左(边的数目= 2)+ 右(边的数目=1)= 3 所以二叉树直径为3 但是这道题有可能不经过根节点,此时根2,左右两边加起来的边数才是最大的=4,所以我们搞一个全局变量。 Entry(); tail = new Entry(); head.next = tail; tail.pre = head; } } // 定义节点
int(intput('>>>') if i // 10000: print(5): elif i // 1000: print(4) elif i // 100: print(3) #限定5位 if a<10: print(1) elif a<100: print(2) elif a<1000: print(3) =input(">>>>") length=len(nnumber) if length>4: print(5) elif length>3: print(4) elif length> 2: print(3) elif length>1: print(2) else: print(1) number=int(input("输入一个不超过5位的正整数:") if 反复迭代20次左右返回的就是n的平方根。
双指针一个left,一个right,同向向后滑动即可.窗口大于x就缩紧left向后移动,窗口小于x就扩大right向后移动.注意,left的下标从0开始,输出位置时要加1,right表示的是和left的相对距离 题目详情: 本题详情如下图: 题目思路: 本题解题思路如下: 一开始输入的时候把奇数直接加到sum里,偶数直接push进大根堆,后面循环k次(条件是堆不为空)把堆顶的数据除2,堆顶除 2后还是偶数就继续push进堆,如果不是偶数就直接加进sum.循环完毕把堆里剩下的偶数加进sum后输出即可. cout<<sum; return 0; } 结语 说点啥好呢...这两道题都不难,主要是手生,一时半会就ac不出来,有思路可以,下次画个图可能会更直观明了一点.以及之前手撕的STL 没想到还有漏网之鱼,后续有空记得补一篇优先级队列的使用说明手册哈.
/** * val list = listOf(1,2,3) * println(list) --- 触发了 toString()的调用 * 默认输出 [1,2,3 ---------------*/ // 当你创建一个函数列表的时候,可以传任意个人的参数给它 val listOf = listOf(2, 3, 4, 5, 6) User(2, "haha", "china")) 总结 Kotlin 没有定义自己的集合类,而是在Java集合类的基础上提供了更丰富的API。 Kotlin 可以给函数参数定义默认值,这样大大降低了重载函数的必要性,而且命名参数让多参数函数的调用更加易读。 Kotlin 可以用扩展函数和属性来扩展任何类的API,包括在外部中定义的类,而不需要修改其源代码,也没有运行时的开销。 中辍调用提供了处理单个参数的,类似调用运算符方法的简明语法。
单向链表 单向链表也叫单链表,是链表中最简单的一种形式,它的每个节点包含两个域,一个信息域(元素域)和一个链接域。这个链接指向链表中的下一个节点,而最后一个节点的链接域则指向一个空值。 ? 表元素域elem用来存放具体的数据。 链接域next用来存放下一个节点的位置(python中的标识) 变量p指向链表的头节点(首节点)的位置,从p出发能找到表中的任意节点。 ?
---- 摘自传智播客公开课 ---- package test; import java.util.Scanner; public class Arithmetic3 { //题设 :某门户网站,具有如下业务功能 // 客户输入个人信息时,当输入年龄,会根据输入的年龄值 // 显示其所属年龄段 90 ~ 99 老老老年 */ //问题:上述业务日均访问量超百万次,设计完成上述功能的程序 break; case 2: System.out.println("青年"); break; case 3:
在快排中,需要归位函数,来判断左右两边的元素大小,先回顾下归位函数 ? 你能发现它是在某个区间内交换位置,也采用了标志位的做法,那就是先取最左边的元素。 应用到排序中,把列表分成一个元素一个元素的,一个元素当然是有序的,将有序列表一个一个合并,最终合并成一个有序的列表。 ? 直接上码啦~ ? 函数调用
时间资源 上一篇,我们知道了如何用循环不变式来证明算法的正确性,本篇来看另一个重要方面:算法分析。分析算法的目的,是预测算法所需要的资源。 答案是必须有一个稳定的硬件模型。在此基础上,才能屏蔽掉硬件配置不同导致的算法运行时间的差异,从而单单显露出算法本身的优劣。 算法分析的环境模型 《算法导论》中,明确的定义了该模型:通用的单处理器/RAM计算模型(RAM,随机访问)。这是大多数讲算法的书里没有提到的重要前提。 所有算法的运行,都基于上述环境模型,比较的基础就有了。 算法分析基础 算法分析的两个重要概念就是输入规模和运行时间。 输入规模 拿插入排序举例,排序1000个数肯定比排序10个数需要更长的时间。 《算法导论》明确的解释说,我们大多数时候应该关注最坏情况的运行时间,理由是: 最坏情况给出了任何输入运行时间的一个上限(做最坏的打算); 对某些算法,最坏情况经常出现,比如检索一条不存在的信息; “平均情况
} Console.WriteLine("JavaToCs OK"); } } } unity3d
在数字化时代,学习工具层出不穷,但很少有工具能像 MarginNote 3 那样彻底改变我们的学习方式。 今天,我们就来深入了解 MarginNote 3 的魔力所在。 1. 阅读模式:为学习量身定制 MarginNote 3 提供了两种阅读模式:文档阅读和主题阅读。 在边缘显示笔记:一目了然 MarginNote 3 允许你在书籍内容旁边直接做笔记,这样你可以在阅读的同时,直观地看到自己的思考和注释,而不会打断阅读流程。 3. 结语 MarginNote 3 不仅仅是一个阅读和笔记工具,它是一个全方位的学习伴侣。通过其强大的功能,MarginNote 3 帮助你更高效、更系统地进行学习。 如果你正在寻找一个能够提升学习效率的工具,MarginNote 3 绝对值得一试。
前言 标记清除算法(Mark-Sweep)是一种非常基础和常见的垃圾收集算法,该算法被J.McCarthy等人在1960年提出并成功的发明并应用于Lisp语言。 这2个名词经常在垃圾收集算法中出现。 collector指的就是垃圾收集器。 mutator是指除了垃圾收集器之外的部分,比如说我们的应用程序本身。 算法原理 标记清除算法将垃圾回收分为2个阶段,标记阶段和清除阶段。 一种可行的实现是,在标记阶段首先通过根节点,标记所有从根节点开始的可达对象。因此,未被标记的对象就是未被引用的垃圾对象。然后在清除阶段清除所有未被标记的对象。 存在问题 标记清除算法最大的问题是存在大量的空间碎片,因为回收后的空间是不连续的。在对象的堆空间分配过程中,尤其是大对象的内存分配,不连续的内存空间的工作效率要低于连续的空间。 ?
兼容性 CSS3为我们提供了一个强大的功能自定义属性,也就是变量,他能让我们更改色系、皮肤、自适配变得简单。 查看兼容性 https://caniuse.com/? search=-- 可以看出94%的用户的浏览器都兼容这个新特性了。 定义使用 变量的定义使用--name,而变量的调用使用var(--name)。 示例 /* 定义全局变量 */ :root{ --navColor: #c00; --navPadding: 10px; } /* 定义局部变量 */ .mdiv{ --boxBorder border: var(--borderWidth) var(--borderColor) var(--borderStyle); border: var(--border); } 其中 :root定义的是全局的变量 ("--variableName"); // 获取样式表里定义的变量 getComputedStyle(element).getPropertyValue("--variableName"); //
自定义事件 除了系统自带的原生 DOM 自带的事件之外,有时候我们需要用到这些自带的事件之外,我们就必须要自定义事件了。 事件名 不同于组件和 prop,事件名不存在任何自动化的大小写转换。 而是触发的事件名需要完全匹配监听这个事件所用的名称。 举个例子,如果触发一个 camelCase 名字的事件,我们还是接着昨天的项目继续往下写,在 TestCom.vue 使用 button 按钮点击事件分发一个 click-event 事件,不同于组件和 定义自定义事件 继续上面的代码,可以通过 emits 选项在组件上定义已经发出的事件: <template>
主函数框架 DES 函数 传入参数为 text(明文 或者 密文) key (解密的key) flag (是加密还是解密过程) # DES 算法实现 flag是标志位 当为-1时, 是DES解密, 各种置换矩阵的定义 DES有各种置换矩阵的定义, 所以提前定义好, 但是这里虽然说是矩阵 但是使用数组来表示的 # S盒 的置换矩阵 S_MATRIX = [(14, 4, 13, 1, 2, 15, 10, 13, 15, 3, 5, 8, 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11)] # P置换的置换矩阵 P_MATRIX R0 += key[i - 1] assert len(L0) == 28 assert len(R0) == 28 #轮函数生成 48位密钥 #定义轮数 Movetimes = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1] #定义返回的subKey retkey = []
尝试分析一下这个算法的时间复杂度,就会发现不容易分析。 ,复杂度起码也有 O(N^3) 吧。 所以这个算法并不好,复杂度太高,且已经无法优化了。 这也就说明,这样定义「状态」是不太优秀的,下面我们换一种定义 dp 的思路。 第二种思路 这种思路稍微有点复杂,但是效率高。 明确了这一点,可以通过这两种情况来设计算法: int[] dp = new int[N + 1]; // 定义:dp[i] 表示 i 次操作后最多能显示多少个 A for (int i = 0; i < 根据这个事实,我们重新定义了状态,重新寻找了状态转移,从逻辑上减少了无效的子问题个数,从而提高了算法的效率。
今天来谈一种十分重要的堆排序的算法,其在STL中的数据结构也就是Priority_Queue。 也是一种十分高效的排序方式,虽然其算法模型为二叉树结构,但是可以使用数据进行模拟这个二叉树的结构和相应的函数操作! 大根堆和小根堆 堆树的定义如下: 堆树是一颗完全二叉树 堆树的当前节点总是不大于或者不小于其孩子节点的值,如果不大于其孩子节点,叫做小根堆。 大根堆和小根堆 那么我们知道了堆的特性之后,我们就可以使用堆的结构对一个列表进行排序,通常为了编程和实现简单,我们会使用数组来模拟堆结构,假设原始数组为a={4,1,3,2,16,9,10,14,8,7 (重点),请关注我的个人公众号 (算法工程师之路),回复"左神算法基础CPP"即可获得,并实时更新!
交易逆序对的总数 - 力扣(LeetCode) 题目分为三个部分讲解,一是题目解析,二是算法原理,三是算法编写,那么,话不多说,直接进行主题咯。 归并排序 题目解析 其实这个题目我们已经在分治1里面做过了,但是在分治1里面使用的是快排,本文介绍分治的另一种算法,即归并排序。 直接就进入原理吧! 算法原理 对于归并排序来说,基本思想是将数组不断的划分,不断的划分,直到划分到了一个数的情况,这么做的原因是为了后面方便合并数组,你想,如果存在两个有序数组,我们想要合并这个有序数组是不是十分容易? 那么对于归并算法同理,我们将数组不断的划分,不断的划分,直到划分为一个元素,此时,我们将该元素视为有序的,所以分治的第一步就完成了,我们应该递归回去了。 那么对于归并排序来说,是将左右划分,并排好序,最后合并,这其实就是树的后序遍历: 对于快排来说,是先确定好了一个元素的位置,然后排序左右两边,这实际上是一种前序遍历: 现在直接算法编写吧!
即,如何快速找到,同时存在于文件a和文件b中的最长子串。算法导论上的LCS(公共子序列)算法并不是很适合我,因为COPY只是去借数据,并不在乎这块数据在哪个位置。 最终发现,有序后缀数组更符合我的需求,空间复杂度极底,并且可以以lg(n)的时间复杂度来快速完成匹配。但是其生成算法DC3,我搞了将近2周才总算搞明白。 整个算法一共就分4步,原始数据在buf中,长度为N,(这里仅粗略描述): 1. 将(i % 3 != 0, i >= 0 and i < N)的值取出来放到一个数组SA12中. 2. 这算法并不是通常见到的,如快排,二分查找,甚至红黑树那么直观。他神奇到,我完全不知道这是在做什么,后缀数组已经排完序了。 在看这个算法时,在第2步我有几个很大的疑惑。 ---- 搞明白之后发现,整个算法的核心思想就是”收敛”, 运用递归的思想不断的收敛,直到比如结果为止。