Github: https://github.com/MyGitBooks/niubility-algorithm
本文档是做者小傅哥经过从leetcode 剑指offer 编程之美 等资料中收集算法题目并加以逻辑分析和编码搞定题目,最终编写资料到本文档中,为你们提供在算法领域的帮助。若是本文能为您提供帮助,请给予支持(加入、点赞、分享)!java
在这以前我基本没怎么关注过leetcode
,仍是最近有人常常说面试刷题,算法刷到谷歌上班去了。我才开始了解下,仔细一看原来虽然没关注过,可是相似的题仍是作过的而且还买过一本《编程之美》的书。git
在 leetcode-cn.com 中每一个算法题都有编号;1 2 3 ... 1566
,并且还在增长。你我都是新人,既然没了解过那就从第一题开始吧,尝试从算法中吸收一些创新的思路。不然为何那么多公司面试招聘都会去考下算法!谷歌``````字节跳动``````腾讯``````阿里``````等等
github
对于这个算法题来讲我仍是蛮喜欢的,由于我是属于那种很偏科的男人,一般数学:140
分,英语:40
分(当年)。好!理由找好了,开始刷个题。据说数学好的男人都不简单! 因此我打算接下来按期的作一些算法题,同时将个人思路进行整理,写成笔记分享给新人,一块儿从算法中成长。web
时间复杂度能够说是算法的基础,若是不在意时间复杂度,那么没有 for
循环解决不了问题!而咱们通常所说的时间复杂度以及耗时排列包括;O(1)
< O(logn)
< O(n)
< O(nlogn)
< O(n^2)
< O(n^3)
< O(2^n)
< O(n!)
< O(n^n)
等。那么一段代码的耗时主要由各个行为块的执行次数相加并去掉最小影响系数而得出的,接下来先看下这种东西是如何计算出来的。面试
代码块算法
int n = 10; for (int i = 0; i < n; i++) { System.out.println(i); }
序号 | 代码块 | 耗时 |
---|---|---|
1 | int n = 10 | 1 |
2 | int i = 0 | 1 |
3 | i < n | n + 1 |
4 | i++ | n |
5 | System.out.println(i) | n |
最终耗时:编程
sum = 1 + 1 + n + 1+ n + n = 3n+3 = n (忽略低阶梯)
从公式和象限图中能够看到,当咱们的公式3n+3
,随着 n 的数值愈来愈大的时候,常数3就能够忽略低阶梯不记了。因此在这段代码中的时间复杂度就是;O(n)数组
所谓低阶项,简单地说就是当n很是大时,这个项相对于另一个项很小,能够忽略,好比n相对于n^2,n就是低阶项数据结构
代码块svg
int sum = 1, n = 10; while (sum < n) { sum = sum * 2; }
最终耗时:
这回咱们只看执行次数最多的,很明显这是一个 2 * 2 * 2 ··· n
,大于 n 跳出循环。
那么咱们使用函数;2^x = n,x = logn,就能够表示出总体的时间复杂度为 O(logn)
好!结合这两个例子,相信你对时间复杂度已经有所理解,后面的算法题中就能够知道本身的算法是否好坏。
https://leetcode-cn.com/problems/two-sum/submissions/
给定一个整数数组 nums
和一个目标值 target
,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你能够假设每种输入只会对应一个答案。可是,你不能重复利用这个数组中一样的元素。
示例:
给定 nums = [2, 7, 11, 15], target = 9 由于 nums[0] + nums[1] = 2 + 7 = 9 因此返回 [0, 1]
java
class Solution { public int[] twoSum(int[] nums, int target) { // todo } }
这是leetcode的第一题,难度简单
,其实若是要是使用两层for循环嵌套,确实不太难。可是若是想战胜99%的选手仍是须要斟酌斟酌算法。
先不考虑时间复杂度的话,最直接的就是双层for
循环,用每个数和数组中其余数作家和比对,以下;
能够看到这样的时间复杂度是;n*(n-1) ··· 4*三、4*二、4*1
,也就是O(n^2),有点像九九乘法表的结构。
代码:
public int[] twoSum(int[] nums, int target) { int[] idxs = new int[2]; for (int i = 0; i < nums.length; i++) { for (int j = i + 1; j < nums.length; j++) { if (nums[i] + nums[j] == target) { idxs[0] = i; idxs[1] = j; } } } return idxs; }
耗时:
为了把这样一个双层循环简化为单层,咱们最能直接想到的就事放到 Map 这样的数据结构中,方便咱们存取比对。那么这样的一个计算过程以下图;
代码:
public static int[] twoSum(int[] nums, int target) { Map<Integer, Integer> hashMap = new HashMap<Integer, Integer>(nums.length); for (int i = 0; i < nums.length; i++) { if (hashMap.containsKey(target - nums[i])) { return new int[]{hashMap.get(target - nums[i]), i}; } hashMap.put(nums[i], i); } return new int[]{-1, -1}; }
耗时:
containsKey
与 get
是否为 null 相比哪一个快吗?若是说想把咱们上面使用 Map 结构的地方优化掉,咱们能够考虑下 Map 数据是如何存放的,他有一种算法是自身扩容 2^n - 1 & 元素,求地址。以后按照地址在进行存放数据。那么咱们能够把这部分算法拿出来,咱们本身设计一个数组结构,将元素进行与运算存放到咱们本身定义的数组中。以下图;
int[] nums
,32是咱们设定的值,这个值的设定须要知足存放大小够用,不然地址会混乱。011111
与每个数组中的值进行与运算,求存放地址。+1
,由于默认数组是0,若是不加1,就看不到位置了。最终使用的时候,能够再将位置结果 -1
。代码:
public static int[] towSum(int[] nums, int target) { int volume = 2048; int bitMode = volume - 1; int[] t = new int[volume]; for (int i = 0; i < nums.length; i++) { int c = (target - nums[i]) & bitMode; if (t[c] != 0) return new int[]{t[c] - 1, i}; t[nums[i] & bitMode] = i + 1; } return new int[]{-1, -1}; }
耗时: