ARTS 是耗子哥(左耳朵耗子)发起的一个活动。 A -- Algorithm 每周一道算法题 R -- Review 每周阅读并点评一篇英文技术文章 T -- Tip 每周学习一个技术技巧 S -- Share 每周分享一篇技术文章 坚持一年 :-) 这是我的第一周 ARTS
Two Sum Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
先审题,要从一个数组里面找到满足想加等于某个数的两个数的位置。第一想法是遍历两遍,这样肯定可以,但是时间复杂度是 O(n^2),我们直接想想有没有更优化的方法。
一般做这种需要多重循环的,有两个可以考虑的优化方向: 1. 不要让这次循环浪费,记录下这次循环的值,后面可能有用; 2. 缩小每次需要循环的集合。
这里刚好适合第一种,把循环了又暂时没用到的值作为字典的 key 中,把位置作为对应 key 的 value。
这里还有个问题,数组的值可能是重复的,但下标不同,如果放到字典里,后面的值和位置会替代前面的值。于是有了下面这种,用了两个字典实现。
var twoSum = function (nums, target) {
var numDict = {},
numDictRepeat = {}
for (var i=0; i<nums.length; i+=1) {
// numDict[nums[i]] = i; // key: integers, value: the indice position
var curVal = nums[i]
if (typeof numDict[curVal] !== "undefined") {
numDictRepeat[curVal] = i
} else {
numDict[curVal] = i
}
var num2Pos = numDict[target - curVal]
if (typeof num2Pos !== "undefined" && num2Pos !== i) {
if (num2Pos === i ) {
num2Pos = numDictRepeat[target - curVal]
}
return [i, num2Pos].sort()
}
}
return []
};
var nums = [3,3], target = 6
rs = twoSum(nums, target)
console.log(rs)
可以看到,用两个数组其实非常搓,只是为了规避两个数相同的情况,那么可以稍微改一下,当相同并且满足相加条件的时候就 return。
var twoSum = function (nums, target) {
var history = {}
for (var i=0; i<nums.length; i+=1) {
var curVal = nums[i]
if (typeof history[curVal] !== "undefined" && (curVal + curVal) == target ) {
// 同数不同位置,想加又刚好是 target 3+3=6
// if (curVal + curVal == target) {
// return [history[curVal], i]
// }
return [history[curVal], i]
} else {
// key: integers, value: the indice position
history[curVal] = i
}
var num1 = nums[i],
num2Pos = history[target - num1]
if (typeof num2Pos != "undefined" && num2Pos != i) {
return [num2Pos, i]
}
}
return []
};
// [3,3],[3,2,4]
var nums = [2, 7, 11, 15], target = 9
rs = twoSum(nums, target)
console.log(rs)
写完再看一下,上下其实有两段一样的 if 判断,造成这样原因是我过于聚焦把循环的值给放到字典里面了,其实可以现判断是否满足条件,满足就搞定返回,不满足才记录到字典里。
于是有了下面这种写法。
var twoSum = function (nums, target) {
var history = {}
for (var i = 0; i < nums.length; i += 1) {
var curVal = nums[i],
num2Pos = history[target - curVal]
if (typeof num2Pos != "undefined") {
return [num2Pos, i]
}
history[curVal] = i
}
return []
};
// [3,3] = 6, [3,2,4] = 6
var nums = [2, 7, 11, 15], target = 9
rs = twoSum(nums, target)
console.log(rs)
总结一下,为什么一开始就抛弃了两重循环的想法,因为我觉得一个工程师是应该有识别方案好坏的能力,一些一看就不是最好的办法,只是在实在没有别的方案的时候才采用,如果能一眼就看出某个方案的好坏,何必把好方案和坏方案放在一起优良中差的硬比一遍呢。
除此之外,虽然知道如何做是更优的方法,但也要跳出方法,回归整体逻辑,不要像这题一开始那样由于关注点过于集中在把数值记录在字典中,从而绕了些弯路。不过经历了把代码逐渐优化写短的过程,还是有点小开心的。
The deepest reason why modern JavaScript frameworks exist
这篇文章把 Vue, React, Angular 称为现代前端框架,主要用一个用户输入 email 地址,列表记录和修改地址的例子来说明『现代前端框架出现主要是为了解决页面状态和 UI 的同步问题』。
主要说了以前的方式会有很多代码用在 HTML 的拼接生成上,易出错以及难以维护。
然后用了一个小例子来描述如何利用 virtual-dom 来更新页面的过程,这个例子对理解组件更新有一些启发。
说说个人观点,更方便的『页面状态和 UI 的同步』只是现代前端框架带来的一个小功能,没有这些框架之前也可以做到正确的渲染,比如用一个函数专门负责渲染,或者 Handlebar.js 等模板库,毕竟正确的渲染是最基本的功能。
我个人理解现代前端框架的出现,给前端带来了组件化,工程化,性能(比如 virtual-dom)上更多的思考,同一个框架组件有更统一的标准和 API,也为前后端分离提供了更加通用的解决方案。
至于原因,我觉得是前端技术演化的结果。随着科技的发展,PC 或手机性能越来越强,前端页面所承载的功能也越来越多,有点像安迪-比尔定律(硬件提高的性能,很快被软件消耗掉了),业务复杂度的上升和对良好用户体验的不断追求带来前后端分离,首屏渲染,异步加载,单页面应用等需求,于是产生了一批代码维护更加便捷,组件化更加清晰的现代前端框架。至于『页面状态和 UI 的同步』,是在这过程中是为了提高生产力,把前端从频繁又繁琐的页面更新上解放出来的一剂良药。
本周记录一个工作中遇到的小 Tip。
工作中有一次把一个分支,称为 feature1,merge 到主干的时候,发现发生了 fast-forward,没有合并记录。原因是不久前 feature1 分支合并过主干,主干和 feature1 不存在分叉,但主干落后于 feature1,所以就直接 fast-forward 了,没有产生新的 commit。
工作中我们是要求主干上的合并都要留有记录,可以用命令 git merge --no-ff
来禁止本次 merge 的 fast-forward。
有时候你可能会忘了添加 --no-ff
参数,也可以设置一下,git config branch.master.mergeoptions "--no-ff"
直接 merge 的时候禁止 fast-forward。
近日,Airbnb 宣布弃用 React Native,原文地址 Sunsetting React Native
我现在是做移动端 web 开发,混合 APP 是主要的业务形态。
我以前用过一下 PhoneGap (后改名 Cordova),上上年也在公司内尝试使用 React Native,最近一些业务上还尝试了 Weex。JavaScript 一统世界似乎一直是前端界不断努力的方向,然而总是不尽如人意。现在依稀能回忆起第一次听说 React Native 时那种兴奋,以及第一次跑通 React Native 例子时感叹其开发体验之好。
然而,事实总是残酷的。软件工程中时间,成本,质量三者最多能做到两个,有时候是一个,有时候一个都做不到。使用 React Native 后,相比终端来讲,做页面的时间肯定是快了,基本兼容 iOS/Android 成本也低了,但是质量却不太行,主要遇到几个问题:
1. 坑比较多,有平台兼容性的,有 React Native 本身的,缺少完善的 debug 工具,debug 耗费的时间较多;
2. 以前终端提供给 web 的接口,需要再封装一遍提供给 React Native;
3. React Native 相比纯终端,能力上都有很大限制,有些能力不具备的时候,需要终端提供支持,比如视频播放器,音频播放器;
4. 我们比较注重页面打开速度,React Native 首次进入的速度不如做了一些优化的 Web,二次进入的速度会好很多,
5. 一些比较复杂的交互无法实现,动画能力较弱。
除了以上问题,React Native 的确在 View 层的开发便捷性上要比原生好不少。如果你想要用在项目中,最好根据实际需求场景,在一些交互要求不高,View 层以展示为主的二级页面上先进行试水。
最后在多唠叨几句,不管 React Native 又或者阿里推出的 Weex,前端只是 View 层,实际上都是靠终端驱动的技术,要想真正用好他们,还需要同时熟悉 iOS 和 Android 相关知识。现阶段,前端想靠 JavaScript 一统天下还是不现实的,所以前端程序员们还是要拓展自己的能力边界,不要整天就会写页面写 CSS,比如学以学习原生开发 :-)