
我也看过之前的刷题的帖子,t/910785 ,我也是觉得这种如果没有额外的项目去做,练练手保持一下感觉也是不错的。 另外说一下,访问 leetcode.com 隔一段时间刷新就会跳到 leetcode.cn ,很烦。
]]>
]]>每次打开 leetcode ,面对近两千道题目,顿时感觉意义全无。我们为什么需要刷算法呢,而且为什么要去刷那些 difficult 难度的题?
]]>
我的代码:
public class Solution { public ListNode detectCycle(ListNode head) { if(head == null || head.next == null) return null; ListNode fast = head; ListNode slow = head; while(fast != slow){ if(fast == null || fast.next == null){ return null; } slow = slow.next; fast = fast.next.next; } fast = head; while( fast != slow){ fast = fast.next; slow = slow.next; } return fast; } } 无法通过的 case:
输入 [3,2,0,-4] 1 输出 tail connects to node index 0 预期结果 tail connects to node index 1 可以通过的代码:
public class Solution { public ListNode detectCycle(ListNode head) { if(head == null || head.next == null) return null; ListNode fast = head; ListNode slow = head; while(true){ if(fast == null || fast.next == null){ return null; } slow = slow.next; fast = fast.next.next; if(fast == slow) break; } fast = head; while( fast != slow){ fast = fast.next; slow = slow.next; } return fast; } } ]]>有没有什么方案可以有代码提示+补全+调试方便的
]]>这道题目为什么贪心算法是最优解?怎么证明。
]]>毕竟随机结果没法直接对比标准答案,感觉要判断正误就挺麻烦,但 leetcode 可以测出来正确性。
有时候写出来某个细节差个+1 -1 ,那概率分布也差不太多吧,它也能测出来有错误不给 AC 。 感觉就挺神奇的。
没有搜到有对这个的讨论。。
]]>int search(vector<int>& nums, int target) { int lo = 0, hi = nums.size(); while (lo < hi) { int mid = (lo + hi) / 2; double num = (nums[mid] < nums[0]) == (target < nums[0]) ? nums[mid] : target < nums[0] ? -INFINITY : INFINITY; if (num < target) lo = mid + 1; else if (num > target) // 这儿为什么不能 -1 ? hi = mid; else return mid; } return -1; } 这个解法省了我几万个脑细胞, lo = mid + 1 很好理解, 但是 hi = mid - 1 为什么出错, 我想破脑袋都没想明白...
leetcode-cn 的题目内容以及顺序和 leetcode 的是完全一样的么?
]]>题目比较简单,只有 010101... 或者 101010... 两个方案 最开始我的解法是:
func minOperations(s string) int { count1 := 0 // 010101... for i := 0; i < len(s); i++ { if int(s[i] - '0') != i % 2 { count1++ } } count2 := 0 // 10101010... for i := 0; i < len(s); i++ { if int(s[i] - '0') != (i+1) % 2 { count2++ } } return min(count1, count2) } 后来发现更简单一点的
func minOperations(s string) int { count1 := 0 // 010101... for i := 0; i < len(s); i++ { if int(s[i] - '0') != i % 2 { count1++ } } return min(count1, len(s)-count1) } 我的疑问是如何用数学证明这两种方案加起来刚好是 s 的长度。
]]>
这次刷的基本是中等题,按题目列表顺序刷的,还是以刷够量为主。想法也很简单,量变才能有质变,先攒够经验再说,这样后面遇到类似的题会有点印象,看题解不会太吃力。另外写刷题总结时也有足够的样本。功利心肯定也是有的,为了以后能在简历上贴个 LeetCode 主页加加分。
后续打算换个刷题思路,除了每日打卡和强化同类题训练,不再刷新题了。一是以前刷过的题肯定忘得差不多了,需要回头再复习一下,二是打算写写刷题总结,总结一下典型题的套路,背一些常用的代码,争取再遇到同类题时都能做出来。三是打算重新学一遍数据结构和算法,和刷题总结融合在一起。
目前仍然感觉自己是个菜鸟,毕竟困难题基本做不出,中等题差不多一半以上还要参考评论或题才能写得出。
下一个目标,明年 3 月份刷够 300 题,总结出一份刷题总结,包含常见的数据结构和算法,典型题分类及解题思路,题目列表及标签。
这一两天还有个打算,想整理一份跳槽后端开发的知识清单出来,目前身处传统行业,感觉没什么前途,明年想转 C++或 Go 的后端开发,到时候 V 站发出来请各位朋友把把关。
]]>
]]>class Solution { public List<String> summaryRanges(int[] nums) { List<String> ret = new ArrayList<String>(); int i = 0; int n = nums.length; while (i < n) { int low = i; i++; while (i < n && nums[i] == nums[i - 1] + 1) { i++; } int high = i - 1; StringBuffer temp = new StringBuffer(Integer.toString(nums[low])); if (low < high) { temp.append("->"); temp.append(Integer.toString(nums[high])); } ret.add(temp.toString()); } return ret; } } 可以看到官方使用 StringBuffer 来连接字符串,在不需要考虑线程安全的环境,毫无疑问可以使用 StringBuilder 来代替,但是问题在于,为什么使用+号来连接的效果会比 StringBuilder 及 StringBuffer 差?
String temp = Integer.toString(nums[low]); if (low < high) { temp += "->" + Integer.toString(nums[high]); } ret.add(temp); 两者的差别为:
StringBuilder/StringBuffer:执行用时:0 ms, 在所有 Java 提交中击败了 100.00%的用户
+号连接:执行用时:8 ms, 在所有 Java 提交中击败了 70.02%的用户
同样的代码在本地各循环 1000 万次的结果为
JDK 1.8(与 LeetCode 相同):7708ms vs 7287ms
JDK 11:7320ms vs 4511ms
>100000了,然后昨天通过了几题,今天发现又涨了点,不错不错,刷题这种无聊的事情,难得有点正反馈,后面以这个排名为目标也可以激励一下自己,目前数据如下:
]]>Remember to enter the code "THANKS2020" at checkout. To take advantage of this limited-time offer, subscribe before 11:59pm PST on Monday, November 30.
降价$30,国内不清楚。有需要的请尽快订阅~
]]>https://leetcode-cn.com/problems/partition-equal-subset-sum/
先贴原题:
给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。 注意: 每个数组中的元素不会超过 100 数组的大小不会超过 200 示例 1: 输入: [1, 5, 11, 5] 输出: true 解释: 数组可以分割成 [1, 5, 5] 和 [11]. 如[1,1,3] 即分割成[1][1],3 弃置 目前就想到暴力算法复杂度 3 的 n 次方 ,求个优化思路或者更优方案, 去重 /回溯的剪枝目前也没好的办法
]]>BST, BFS, DFS 都有这类题,不会套输入就完全做不了
求有经验的老哥指点一下
]]>感觉自己刷题总是会动力不足或者三天打鱼两天晒网
于是跟几个小伙伴建了一个 LeetCode 刷题打卡群,连续打卡有奖励的那种!
感觉大家一起刷题会更有动力一点~
然后如果有不会的问题欢迎在群里提问,也欢迎帮助解答其他人的问题~
想入群的小伙伴可以加我的微信 [ evelynxxyy ] ,备注 [刷题]
]]>rand7 可生成 1 到 7 范围内的均匀随机整数,试写一个方法 rand10生成 1 到 10 范围内的均匀随机整数。 不要使用系统的Math.random()方法
rand7 已定义。n 表示 rand10 的调用次数。在线评测地址:LintCode 领扣
样例 1:
输入:1 输出:[7] 样例 2:
输入:2 输出:[8,4] 样例 3:
输入:3 输出:[8,1,10] [题解]
考点:
题解:
public class Solution extends SolBase{ public int rand10() { while(true){ int rand49 = (rand7() - 1) * 7 + rand7() - 1; if(rand49 <= 39) { return rand49 / 4 + 1; } } } } 更多语言代码参见:九章算法
]]>欢迎在讨论区交流,也欢迎添加微信 longswordMAN 进群+听直播分享,加的时候注明 V2EX 的 id 就行。
我会尽量保持周更的频率,每条评论也会看,比较走心的高质量评论必回,看反馈情况决定是否改变更新频率。
帖子主要谈三个主题:
1.kaggle 的比赛冲分技巧。在具备 top10%乃至 top2%的实力的情况下,如何百尺竿头更进一步,冲击奖牌, 以及如何选择适合自己的项目。
2.在简历里怎么包装项目,面试的时候怎么展示,
3.怎么通过 kaggle 来 networking,以文会友,建立人脉网络。
废话不多说,正题开始。
想要在 kaggle 平台制霸,我们首先要了解 kaggle 的前世今生,了解他比赛的风格和最近几年的变化方向。
Kaggle 是一个对给定数据集的的数据建模平台,以分类、回归问题为主(也有少许聚类,我个人估计应该是因为聚类模型的表现衡量起来相对麻烦,以后如果有时间,可以开一个专题讲聚类,现在因为和主线有点偏离,先按下不表)。
Kaggle 竞赛的合作方,早期可以认为是悬赏方,提供数据和任务,往往是公司内部数据(当然作了一些去敏),进而提出公司一个实际需要解决的问题。这个模式有一些类似赏金猎人,尤其是在 2010 年前后,kaggle 还比较小众的时候,参赛人数不多,如果不是金额特别爆炸的悬赏,不是精心雕琢的模型往往也能瞎打乱撞拿到奖金。
虽然一开始 kaggle 的初衷有一些类似于赏金猎人,但是随着平台的壮大,单纯的去冲击奖金的期望收益已经大大降低,所以 kaggle 平台慢慢就演变成了现在类似竞赛平台的形式。尤其是在被 Google 收购之后,kaggle 上的题目风格发生了不小的变化,概括起来就是两多两少。
两多:
参赛队伍变多
深度学习模型变多
两少:
数据量变小
比赛数量变少
]]>我们这里回过头最后再说一个哈希的改编案例,接下来我们要说 array 和 linked list 这对相爱相杀的兄弟。
我们接着来看一个哈希表的例子:老规矩,先上原题:
给定 string 数组把所有“同构词”聚在一起。
例: ["eat", "tea", "tan", "ate", "nat", "bat"], 返回: [
["ate", "eat","tea"],
["nat","tan"],
["bat"] ]
说明:如果两个单词所组成的字母完全相同,只是字母位置不同,我们叫它们 anagrams 。首先我们冷静分析一下,既然是用哈希表,一定要有适合做 key 的元素。那么需要找到适合的 key,应该怎么去做呢?我们观察一下,"tea" 和“ate”只是存在顺序的区别,最终这两个单词能够被映射到同样的”同构词“,也就是我们的 group 需求,把 anagram 全凑起来,我们在每次哈希前,把字母排个序,那么所有 anagram 的哈希值就会一样。我们用 string 做 key,不过需要将 key 排序后的结果当 key,得解。
]]>链表是一个非常经典的数据结构,但是也非常 tricky,而且常见的是令人虎躯一震的空间 in place 要求,由于链表的一些特殊性质,经常会作为一个面试考察的重点。
我们先看一个原题: 从已排序的链表中移除重复的单元,如 1->1->2, 返回 1->2. 1->1->2->3->3, 返回 1->2->3
思路很简单,双指针,指针往后找,碰到相同的数值,继续往后,直到第一个不同的数,讲慢指针的 next 指向快指针,得解。关键在于 one pass,一次过,如果搞个哈希表来做那是画蛇添足,空间上不是最优解,而空间的考察其实是链表的重中之重,所以这就是我一直强调的,哈希虽好,但要慎用。
改编题 1: 给定有序链表,如果某元素出现三次以上的,删除该元素,1->1->2->3->3->3, 返回 1->1->2 我们仍然可以双指针,如果数值不同,快慢指针各走一步 遇到相同数值,快指针继续走,同时计数,超过三次就接着往后,碰到新数字后修改慢指针的 next 。
改编题 2: 但如果改成无序的链表呢? 题目变成:给定无序链表,如果某元素出现三次以上的,删除该元素。 这时候我们要头脑冷静地分析,条件虽然变了,但和之前的题目有什么关联和不同? 这时候不是有序数组,因此相同的数不会团在一起,为了能够方便判断是否有 3 个以上的频次,我们需要借助哈希表去统计出现的频次,key 是链表元素的 value,值是频次。那么第二次遍历的时候,我们把链表的 value 当作 key 去哈希表查找,如果对应值大于三,删除元素,问题得解。算法时间复杂度 O ( n ),空间 O ( n )。
改编题 3: 无序链表,但如果要求重复元素的个数不超过自身的 value 呢,比如:1-> 1-> 2->3->2->2 改成:1 - 2 - 3 - 2 还是用哈希,只不过需要厘清几个值的关系,该 map:key 是链表的 value,value 是链表 value 出现的频次。循环的时候,第一次遍历统计频次,第二次遍历,如果 map ( node.val )> node.val ,删一个节点,并且同时 map 中的 node.val 值减一,算法时间复杂度 O ( n ),空间 O ( n )得解。
其他推荐原题: 两链表交叉点问题 链表环问题 链表 merge sort 问题
这几个经典原题原题的改变我之后在我的荔枝微课直播间都会提及。 可以加微信 longswordMAN 进群+听直播,注明 V2EX 的 id 即可。
]]>