最近两周一直感觉学习比较松懈, 打算加大一点强度, 从今天开始, 希望能在每天正常进度完成后在LeetCode上选一两题写一写, 同时学习一下高手的做法.
LeetCode - Algorithms #001 Two Sum
给定一个整数数组, 找出其中两个元素, 使其和等于目标值, 返回这两个元素在原数组中的索引组成的数组. 可以假设有且只有一组正解, 且每个元素只能使用一次.
我的思路:
其实想不到什么好的方法, 遍历就好, 非常素朴的回答:
1 class Solution { 2 public int[] twoSum(int[] nums, int target) { 3 //根据题目设定, nums的长度必大于等于2, 所以也不用考虑什么特殊情况 4 //结论所需的两个索引, i从0开始遍历, j从1开始遍历 5 int i = 0,j = 1; 6 outer: for(i = 0; i < nums.length - 1; i++){ 7 for(j = i + 1; j < nums.length; j++){ 8 //找到答案就跳出外循环 9 if(nums[i] + nums[j] == target){ 10 break outer; 11 } 12 } 13 } 14 //把答案装进数组然后返回 15 int[] ans = {i,j}; 16 return ans; 17 } 18 }
结果顺利通过, 用时28ms, 只有30%的用户比我还慢, 看来是写得很差了??
再来看看大神们是怎么做的, 柱状图里面最高的那一条是用时4ms的例子, 拿来看看(注释是我加的):
1 class Solution { 2 public int[] twoSum(int[] nums, int target) { 3 //先创建一个长度为2的整数数组用来装答案 4 int[] result = new int[2]; 5 //再创建一个key和value都为整数类型的HashMap 6 Map<Integer,Integer> m = new HashMap<Integer, Integer>(); 7 //从0开始对nums进行遍历 8 for(int i = 0; i < nums.length; i++) { 9 //当i=0的时候,m为空,所以下面if语句一定无法满足条件,直接走else把(nums[0],0)存入m; 10 //从i=1之后的情形开始,判断目标值减去当前的nums[i]是否已经包含在了m的keySet中, 11 //如果包含,就把它的索引找出放入result[0]中,把i放入result[1]中,跳出循环, 12 //如果不包含,就把(nums[i],i)加入m,继续遍历 13 if(m.containsKey(target - nums[i])){ 14 result[0] = m.get(target - nums[i]); 15 result[1] = i; 16 break; 17 } else { 18 m.put(nums[i], i); 19 } 20 } 21 //返回结果 22 return result; 23 } 24 }
确实是非常有巧思的做法, 几乎没有处理过算法问题, 我无论如何也想不到用HashMap来处理此类问题
在我写的遍历中, i每增加1, 都要对剩下的值进行一一匹配测试, 上面这个写法就用HashMap自带的检验方法测试一次就好了, 而且hash表结构在查找数据的时候速度是非常快的
但是能快这么多我确实没有想到, 现在也不是特别理解??
最后来看看2ms完成的大神飘逸的思路,我花了将近一个小时才读明白,注释写得不怎么清楚,不过尽力了??
大神从头到尾都没有用过超过java基础第一周的内容:
1 class Solution { 2 public int[] twoSum(int[] nums, int target) { 3 //先把原数组的长度存为常数il 4 final int il = nums.length; 5 //System.out.println("array length: " + nums.length); 6 //int il2 = (il >> 2) -1 ; 7 //arrayLen/4 - 1 8 //System.out.println("il2: " + il2); 9 int pot = 4096; 10 //while((il2 >>= 1) > 0) pot <<= 1; 11 //大神拿来做位运算用的常数4095, 也就是二进制的1111 1111 1111 12 //看排名第二的答案, 这个常数是根据数组长度生成的, 这里选4096应该是大神为了冲击最快手动选定的最适合本题的长度 13 final int bitMod = pot - 1; 14 15 16 //System.out.println("bitMod: " + bitMod); 17 //System.out.println("pot: " + pot); 18 19 20 //一个长度为4096的"篮子" 21 final int[] bucket = new int[pot]; 22 //一个与原数组长度相同的"链" 23 final int[] linked = new int[il]; 24 //把原数组第一个元素存为常数 25 final int firstVal = nums[0]; 26 //从第二个元素开始遍历 27 for (int i = 1; i < il; i++) { 28 //目前的元素值 29 int currNum = nums[i]; 30 //目标值和元素值的差 31 int complement = target - currNum; 32 33 //如果差恰好等于第一个元素值,就直接返回结果 34 if (complement == firstVal) { 35 return new int[] { 0, i }; 36 } 37 38 //这里的complement & bitMod的结果应该是等价于 39 //i > 4095 ? (i % 4096) : i 或者其实也就是 40 //i % 4096 41 //只不过运行效率要快得多 42 //所以这里做得就是把差值对4096的余数放在这个篮子里 43 int complementLLIndex = bucket[complement & bitMod]; 44 45 //当这个篮子里对应的位置不为空的时候, 就证明这个所需要的差值(或与之同余的值)之前已经遍历过了 46 while(complementLLIndex != 0) { 47 //这个篮子里面的对应位置应该存储的是所需差值在原数组中的索引,进行检验 48 if(nums[complementLLIndex] == complement) { 49 //Found 50 //如果是就找到了 51 return new int[] { complementLLIndex, i }; 52 } 53 //如果不是, 说明找到的不是对应的差值, 只不过是和对应的差值同余的值, 54 //继续通过linked中存储的上一个索引进行匹配 55 complementLLIndex = linked[complementLLIndex]; 56 //如果都不行说明找到的全都是同余的值,而没有匹配值,跳出循环继续遍历 57 } 58 //使当前值对4096取余 59 int currNumLLIndex = currNum & bitMod; 60 //把篮子中上面那个余数值位置的值存入linked[i] 61 linked[i] = bucket[currNumLLIndex]; 62 //再让篮子里上面那个mod位置的值为i 63 bucket[currNumLLIndex] = i; 64 65 } 66 return null; 67 } 68 }
可能是我少见多怪, 总之就是强
这题就先到这里
LeetCode - Database #175 Combine Two Tables
数据库的第一题
非常简单的连表查询, 只要记得满足题目要求, 记得使用LEFT OUTER JOIN即可:
1 # Write your MySQL query statement below 2 SELECT Person.FirstName, Person.LastName, Address.City, Address.State 3 FROM Person LEFT OUTER JOIN Address 4 ON Person.PersonId = Address.PersonId;
顺利通过:
这题其实也没什么好说的, 大家写得都差不多, 贴一个代表性的回答, 提醒自己对表可以使用别名进行操作:
1 # Write your MySQL query statement below 2 select P.FirstName, P.LastName, A.City, A.State 3 from Person P Left Join Address A 4 ON P.PersonId = A.PersonId;
原文地址:https://www.cnblogs.com/chang4/p/9703872.html