前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >20180701_ARTS_week01

20180701_ARTS_week01

作者头像
Bob.Chen
发布2018-08-02 15:58:26
4760
发布2018-08-02 15:58:26
举报

ARTS 是耗子哥(左耳朵耗子)发起的一个活动。 A -- Algorithm 每周一道算法题 R -- Review 每周阅读并点评一篇英文技术文章 T -- Tip 每周学习一个技术技巧 S -- Share 每周分享一篇技术文章 坚持一年 :-) 这是我的第一周 ARTS

Algorithm

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)

总结一下,为什么一开始就抛弃了两重循环的想法,因为我觉得一个工程师是应该有识别方案好坏的能力,一些一看就不是最好的办法,只是在实在没有别的方案的时候才采用,如果能一眼就看出某个方案的好坏,何必把好方案和坏方案放在一起优良中差的硬比一遍呢。

除此之外,虽然知道如何做是更优的方法,但也要跳出方法,回归整体逻辑,不要像这题一开始那样由于关注点过于集中在把数值记录在字典中,从而绕了些弯路。不过经历了把代码逐渐优化写短的过程,还是有点小开心的。

Review

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

本周记录一个工作中遇到的小 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。

Share

近日,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,比如学以学习原生开发 :-)

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-07-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Algorithm
  • Review
  • Tip
  • Share
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档