2019年08月03很幸运的参加了第一期 《极客大学前端训练营》,成为了winner大神的座下弟子,老师的第一堂课,就让我们自己写出一个黑白棋项目,第一堂课就被虐的很惨,因为黑白棋的吃子逻辑和落子逻辑,理解起来并不难,但是如何用编程语言去将这些逻辑转换成一行一行的代码,我发现自己竟然除了基础的 for if else 之外其余的都不知道怎么去写,最后看着老师逐行写出来之后,看到这些 while for if else 竟然可以这么奇妙的组合在一起来实现这套复杂的吃子、落子、边界等等的判断,而且代码简洁易读,自己除了惊呆还是惊呆,自己默默在心理幻想了一下:这要是我当着一群师妹的面,从头至尾敲出来一套可以跑的黑白棋代码,是件多么帅的事啊!
我们平时主要都是面向业务进行编程,而复杂的业务最终都会被拆分为各个简单的子项问题,每个子项问题公司内部已有封装后的函数或者UI样式库,来服用解决此类问题,我们平时做的就是将新业务拆分子问题,然后去公司的wiki里面寻找已实现的api并复用,很少自己去写一套复杂逻辑(复杂的都是人家架构师或者公司大佬们去写),所以都没刻意练习过,写不出黑白棋逻辑很正常,能力上的缺陷越早发现就意味着有更多的时间和机会去解决这个缺陷。
“编程能力就是将实际业务逻辑转换为对应编程语言规则下的可运行代码能力”。 ——winner
在winner老师的指导下,在我们班长、各组组长的共同组织下,我们成立了刷题小组 LeetCode题库,设定群规每人每周最低刷三道题,刷不够题数的要做一次组内分享,在组织的带领下我们各个成员按照自己感兴趣的标签都开始刷了起来。
1.三分钟热血
我审了一下leetcode网站,发现标签为简单的题目我都写不出代码,我感到很焦虑,我想起上学的时候,数学老师说过: “千古文章一大抄,不会写作文你就死记硬背下来优秀文章,然后你就会写了。” 想想平时工作中也经常遇到难题,产品小姐姐就差把刀驾到我的脖子上催促着快点实现需求,根本不会有太多时间让自己去试错,唯一的方式是搜索已有的可行解,然后复制粘贴到自己的工程中debug后,使用搜索的代码去解决问题。
于是我开始主动看题解,然后根据题目的意思去理解题解中的每一步代码的含义,理解后在去刷类似的题目,尝试自己用刚才 “理解”后的方式去写出类型题的答案,然后去leetcode测试和调试代码的bug,最终实现出来的代码运行效率都差强人意,对比那些好的题解中的代码后,意识到自己的使用数据结构和逻辑判断部分优化的空间很大,然后对照着修改自己的代码,最终在胜率达到 60% 以上,再去刷另一道题。
2.首战告捷
刷题的第一周,组内同事大部分都出去团建了,剩下几个人留守,除了处理常规的线上问题之外,工作压力不大,所以我有大把的机会将部分时间投入到刷题的乐趣中,每刷一道题都感觉像打游戏过了一关,这样我第一周的战绩 29 道,成为我们组内刷题战绩榜首。
3.刷题后的思考
let minWindow = (s, t) => {
if (!s || !t || s.length < t.length) return "";
let [tCount, winCount] = [new Map(), new Map()];
// 统计字符串 t 中每个字母出现的次数
for (let ct of t) tCount.set(ct, (tCount.get(ct) || 0) + 1);
let [left, right, match, start, minLen] = [0, 0, 0, 0, s.length + 1];
let showDiv = null;
while (right < s.length) {
if (match < t.length) {
let rightKey = s.charAt(right);
if (tCount.has(rightKey)) {
winCount.set(rightKey, (winCount.get(rightKey) || 0) + 1);
if (winCount.get(rightKey) <= tCount.get(rightKey)) {
match++;
}
}
mountText("左指针固定,移动右指针!");
showDiv = mountChild();
renderChildren(showDiv, left, right, "2px solid green");
mountArrow(left, right - 1, s);
right++;
}
while (match === t.length) {
if (right - left < minLen) {
mountText(`找到和已有可行解 "${s.substr(start, minLen)}" 比较后的最小可行解:【${s.substr(left, right - left)}】!`, 'blue');
showDiv = mountChild();
renderChildren(showDiv, left, right, "2px solid green");
mountArrow(left, right - 1, s);
start = left;
minLen = right - left;
} else {
mountText(`窗口内字符 "${s.substring(left, right + left - 1)}" 比现有可行解【${s.substr(start, minLen)}】长度大或相等,` + "右指针固定,继续移动左指针!");
showDiv = mountChild();
renderChildren(showDiv, left, right, "2px solid green");
mountArrow(left, right - 1, s);
}
let leftKey = s.charAt(left);
if (tCount.has(leftKey)) {
winCount.set(leftKey, winCount.get(leftKey) - 1);
if (winCount.get(leftKey) < tCount.get(leftKey)) {
match--;
}
}
left++;
if (match < t.length) {
mountText("窗口内元素种类或个数不符合预期,固定左指针,开始移动右指针!");
showDiv = mountChild();
renderChildren(showDiv, left, right, "2px solid red");
mountArrow(left, right - 1, s);
}
}
}
mountText(`右指针已到达边界,循环结束,最小覆盖子串为:"${s.substr(start, minLen)}"!`, 'blue');
return minLen === s.length + 1 ? '' : s.substr(start, minLen);
};
贵在坚持
发明是百分之一的聪明加百分之九十九的勤奋。 ——爱迪生
刷题没有捷径可以走,只有不断的刻意练习,并且真正体会到了那种 “开悟” 那种感觉,才会越刷越快,每个人的闲暇时间数量或知识体系都会影响速度,但是只要努力就一定会到达终点,小伙伴们,一起加油!
定个小目标,赚它一个亿。 ——王健林