2018-07

博客分类: 江河计划-月总结

2018-07

算法

给定一个数组,求三堆的和尽量最小。

这不应该去使用贪心算法,做类似瀑布流的一个布局。应该用完全背包算法。

贪心算法解法

var input = [1,2,3,4,5,6,7,8,9]

const findMinIndex = function (arr) {
    var min = arr[0];
    var minIndex = 0;
    for (var i = 1; i < arr.length; i++) {
        if (min > arr[i]) {
            minIndex = i;
            min = arr[i];
        }
    }
    return minIndex
}

Array.prototype.findSum = function (n) {
    if (n > this.length) return false;
    var arr = new Array(n);
    var sumArr = new Array(n)
    for (let i = 0; i < n; i++) {
        arr[i] = [this[i]];
        sumArr[i] = this[i];
    }
    if (n === this.length) return arr;
    for (let i = n; i < this.length; i++) {
        var minIndex = findMinIndex(sumArr);
        arr[minIndex].push(this[i]);
        sumArr[minIndex] += this[i];
    }
    return arr;
}

input.findSum(3);
// output [[1,5,9],[2,6],[3,7],[4,8]]
// 实际上随意想想这也不是最优解

背包解法

const knapsack = (weights:number[], total:number) => {
    var n = weights.length;
    var f = [[]];
    f[-1] = new Array(total+1).fill(0);
    for(var i=0; i<n; i++){
        f[i] = new Array(total).fill(0);
        for(var j=0; j<=total; j++){
            if(j < weights[i]){
                f[i][j] = f[i-1][j];
            }else{
                f[i][j] = Math.max(f[i-1][j], f[i-1][j-weights[i]] + weights[i]);
            }
        }
    }
    var resData = [],
        j = total;
    for(var i=n;i--;){
        if(f[i][j] > f[i-1][j]){
            resData.push(weights[i]);
            j -= weights[i];
            weights.splice(i, 1);
        }
    }
    return {
        resData,
        arr: weights
    }
}

const findSum = (n:number, arr:number[]) => {
    var total = 0;
    arr.map(item => {
        total += item;
    });
    var avg = Math.floor(total / n);
    var resData = [];
    for(var i=0;i<n;i++){
        var res = knapsack(arr, avg);
        resData.push(res.resData);
        arr = res.arr;
    }
}
findSum(3, [1,2,3,5,6,8,9])

验证二叉搜索树

给定一个二叉树,判断其是否是一个有效的二叉搜索树。

假设一个二叉搜索树具有如下特征:

节点的左子树只包含小于当前节点的数。 节点的右子树只包含大于当前节点的数。 所有左子树和右子树自身必须也是二叉搜索树。

var isValidBST = function(root) {
    if(!root) return true
    var arr = [];
    inOrder(root, arr);
    for(let i=0; i<arr.length - 1; i++){
        if(arr[i] >= arr[i+1]){
            return false
        }
    }
    return true;
};

const inOrder = (root, arr) => {
    if(root === null){
        return ;
    }
    inOrder(root.left, arr);
    arr.push(root.val);
    inOrder(root.right, arr);
}

对称二叉树

给定一个二叉树,检查它是否是镜像对称的。

例如,二叉树 [1,2,2,3,4,4,3] 是对称的。

        1
       / \
      2   2
     / \ / \
    3  4 4  3

但是下面这个 [1,2,2,null,3,null,3],则不是镜像对称的

     1
    / \
   2   2
    \   \
     3   3

我的解法是采用先序遍历和后续遍历得到结果,然后对比两个遍历结果,首尾是否相等。

var isSymmetric = function(root) {
    if(!root) return true;
    var arr1 = [];
    var arr2 = [];
    inOrder(root, arr1, arr2);

    var res = arr1.some((item, index) => {
        var _num2 = arr2.pop()
        if(_num2 === undefined || _num2 !== item){
            return true
        }
    });
    if(!res && !arr2.length){
        return true;
    }
    return false
};
const inOrder = (root, arr1, arr2) => {
    if(root === null){
        arr1.push(false)
        arr2.push(false)
        return ;
    }
    arr1.push(root.val);
    inOrder(root.left, arr1, arr2);
    inOrder(root.right, arr1, arr2);
    arr2.push(root.val)
}

更好的解法,在递归的过程中就进行对比,将对比结果直接返回,空间复杂度会降低。

var isSymmetric = function (root) {
    return helper(root, root)
};

function helper(l, r) {
    if (l == null && r == null) return true;
    if (l == null || r == null) return false;
    return (l.val == r.val) && helper(l.left, r.right) && helper(l.right, r.left)
}

二叉树的层次遍历

给定一个二叉树,返回其按层次遍历的节点值。 (即逐层地,从左到右访问所有节点)。

例如: 给定二叉树: [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7

返回其层次遍历结果:

[
  [3],
  [9,20],
  [15,7]
]

我的解法是遍历,逐渐增加深度,依次传递下去,Leecode 解法都类似。

var levelOrder = function(root) {
    if(!root) return [];
    var arr = [];
    inOrder(root, 0 , arr);
    return arr
};

const inOrder = (root, deep, arr) => {
    if(!root) return;
    if(!arr[deep]){
        arr[deep] = [];
    }
    arr[deep].push(root.val);
    root.left && inOrder(root.left, deep + 1, arr);
    root.right && inOrder(root.right, deep + 1, arr);
}

将有序数组转换为二叉搜索树

将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。

本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。

示例:

给定有序数组: [-10,-3,0,5,9],

一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树:

      0
     / \
   -3   9
   /   /
 -10  5

我的解法是递归的过程中加入二分

var sortedArrayToBST = function(nums) {
    if(nums.length === 0) {
        return null;
    }
    if(nums.length === 1) {
        return new TreeNode(nums[0]);
    }
    var mid = parseInt(nums.length / 2);
    var root = new TreeNode(nums[mid]);
    root.left = sortedArrayToBST(nums.slice(0, mid));
    root.right = sortedArrayToBST(nums.slice(mid + 1));
    return root;
};

前端架构

为什么要做前端架构

前端开发的目标是为了实现高效高质量的推进业务。如何让一个团队达到这个目标?需要对现有的目标做一个拆分。我觉得应该分为:业务 + 技术 + 团队 + 后评估系统来完成。

业务

达成业务是目标,那么业务达成分为两方面:拆解现有的业务需求并完成上线;能支撑未来至少半年的业务发展。前端在这方面的贡献在于技术输出,需要用一定的技术能力达成这个目标。也就是说技术方案需要能达成当前的业务需求并能支撑未来的技术发展。

以我负责的业务为例,我们的业务需要满足用户的需求,将业务放在了五个载体上,这就是五个端,这五个端分别承载着不同的用户习惯和目标。微信端是在微信的公信力下构建快捷的服务承接;小程序是承载我们的大背景下不能放在微信服务体系下的商业服务;自助机是在服务落地的地方承接线上无法完成的功能;APP 是形成一定的用户习惯的服务承接;PC 后台系统是要展示更多的信息,便捷的操作处理的功能。不同端之间前端不仅仅是做粘合剂的作用,更多的是提高业务门槛,大统对于前端来说是提升开发效率,工作更加便捷,但是对于业务来说却是降低了实现成本,降低了技术壁垒。

业务的不同侧重点会将服务承接端进行划分,端只是达成业务的方式,而前端在业务中的承载的角色是沟通业务与用户之间的展示和交互,能向用户更加准确的表达业务。

技术

技术要达到上述目标,肯定需要技术架构来作为支撑,技术需要去完成业务目标,并留出足够多的扩展性支撑,这个时候就应该根据业务形态去构建对应的技术架构作为支撑。

对于前端来说这里的考虑会有:其他同类型业务是怎么完成的,我们的业务和别的业务有什么不同,适合用什么端来展示,适合什么样的前端架构,如何和其他技术方合作,使用什么技术框架来完成,周边需要怎样的服务设施,如何与现有的技术方案融合,如何用最低的成本去实现等等,这些考量的问题会最终决定业务应该用什么样的技术架构去完成。

但是技术架构是会先考虑业务的情况,优先考虑业务的实现和未来的扩展,在这个的基础上,还需要考虑团队的情况,因为毕竟一个业务可能会需要协作完成,所以技术架构考虑了业务的基础上,要尽量考虑前端团队最终的输出和完成情况。

团队

我认为技术架构又是由团队的平均水平决定的,不是由最高水平决定,我们可以由最高水平的同学去构建底层和架构,让这个技术架构具有更强的各种性质,但是完成业务需求的同学都是平均水平的同学。

所以核心在于提高团队的平均水平,提高团队的平均水平我认为应该分四个阶段:完成业务,了解为什么要这么去完成业务,知识的广度,知识的深度。

最低的要求是要按照一定的规范去完成业务,这个阶段比较重要的是代码的 review,这样才能控制初期的业务完成度的质量,保证开发质量,加深对于业务的理解。

第二个阶段要让大家明白为什么要这么写,带着思考更好的去完成业务,这个阶段最重要的是设计的 review,明白如何去划分模块,更好的结构化自己的代码,提高开发效率

第三个阶段要对前端相关全链路的知识有一定的了解,前至浏览器网络层,后至如何与其他方配合开发,这个阶段最重要的是了解如何定位问题,bug 和线上的故障一定要更快的找准问题,及时解决问题,出现问题第一时间能解决。

第四个阶段需要进入一些细分领域进行深度的学习,一个团队能承接什么样的业务难度是取决于是整个团队的技术能力深度,不取决于某个人,所以需要让团队分出不同方向,然后每个人进行不同方向的深度学习,然后让团队的木桶尽量深。这个阶段是处理高精尖问题,攻坚一些业务难点和技术难点。

关系

上面由目标引发了这些思考,为了完成目标需要上诉的这三个要素,这三者也需要协同来完成目标。所以三者间一定存在着一些关系。我目前的认知认为三者存在以下关系。

业务的实现及未来发展的扩展性需要技术架构来承接,技术架构的扩展性,容错性,低耦合高内聚一定程度决定的能承接多大多复杂的业务。业务的实现由技术来决定,技术架构越来越完善也能承接越来越好的业务。

技术架构一般是由团队的平均水平决定,技术架构能攻坚的难度是由团队的最高水平决定,前端是推进业务的效率及质量需要的是大部分的人去完成,而后者是难度需要的是极少部分的人去完成,这也需要团队中有良好的人员结构。技术架构的演进也将进一步提升团队成员的技术能力。

团队的好坏一定程度是受业务的好坏决定,通过培训和其他的一些方式只能提高团队的水平,而且团队的稳定性也很难提升,好的业务能吸引更优秀的人员加入,也能让内部的同学不断跟着业务的发展所提升自己。并且优秀的业务也能绝大程度的留住优秀的同学,也将进一步提升团队的稳定性。吸引了更多更优秀的同学,业务的完成度也将越来越好。

三者之间应该是一个相互依赖的关系,都是一个正向促进的作用,但是这种正向促进是需要有定性或者定量的方式来评估的,如何评估这三方面做的好或者不好,才能真正发现问题,不断的优化提升。

后评估系统

我们只有通过一系列的定性或者定量的指标衡量之后才能判断我们的上面这三方面做的好还是不好。

评估业务可以通过常规的一些指标,比如PV、UV、转化率,也有一些特性业务形态的评估指标,比如成单率、停留时间、有效点击等。这些都是可以通过前端交互的改进来提升这些业务指标。

技术评估可以通过定性的指标,业务的扩展性,业务的完成度,完成效率。以及一些线上的白屏时间,资源加载时间,js 报错监控情况等等。这些指标能一定程度的反应前端技术架构的稳定性,扩展性等。

团队评估可以通过一些项目管理的指标来衡量,开发周期中有没有 delay,有没有被测试打回,测试过程中千行 bug 率,上线之后的线上 bug 数,线上故障情况,这些指标来反应团队成员的效率及能力素质。

评估系统仅仅是作为一个参考和衡量,不要忘了我们最终想要达到的目标高效高质量的推进业务。高效高质量是一方面,这里用的是推进业务不是完成业务,前端应该具有交互工程的职能。应该从交互的角度给业务提供建议去迭代改进业务,也需要去协调各方资源,推动业务上线。

总结

架构这个东西很虚,很难让人理解,我目前仅仅是能根据自己的认知将架构这个概念,拆解到这些具象化的代名词上,每一个方向上都应该会有很多延伸,业务又会有适合业务形态的业务架构,技术又会有应用场景的技术架构,团队又会有最大向外输入价值的团队架构。这些概念又需要拆解成实实在在的具象化的代名词进一步进行理解,之后会逐渐去落实。

技术架构

技术为业务服务,在做技术架构的时候更多的是要在成本和业务要求之间寻找平衡。业务要求为第一优先级,在保证业务需求的基础上尽可能的降低成本。这个成本有技术架构本身的成本,也有团队成员的学习成本,还有后期的维护成本,并且技术架构还需要根据未来的业务变化进行不断的演进。最终达成高效高质量的推进业务。

底层建设

面向中小型前端技术团队,在建立技术架构的时候应该从全盘考虑。底层建设指的是选择适合的技术栈 + 脚手架,建立整个基本的开发流程。

技术栈统一有好处也有坏处,好处是降低学习成本,降低项目间的维护成本,并且强依赖单一技术栈也会加强对深度的了解。缺点就是不能全面的衡量一个业务最适合什么样的技术栈,可能有更合适的方案,但是由于技术栈的限制只能退而求其次。

脚手架统一是为了不让团队成员在构建项目时重复的去关注周边环境的设施。通过脚手架统一整个工作流程,让整个工作流程模块化、组件化、自动化、规范化、标准化。并且统一的环境配置可以降低学习成本,在升级的时候也可以做到一处升级全栈业务就进行升级了,但是缺点也是定制化程度降低,一处升级了所有业务,但是也需要大量的回归测试。

站在整体效率上看我比较倾向统一技术栈和底层开发模式,从目前技术架构的实践上来看是达到了我的预期,高效高质量推进业务。

SPA or MPA

这两种架构已经是当前非常主流,优缺点也非常明显的架构了,SPA 单页面,更流程的交互,页面与页面之间的切换,构建富应用能更加得心应手。缺点初始资源加载太大,很多不会被用户使用到的资源也加载进来了。这个可以一定程度上的优化。首次白屏时间较长,并且不利于爬虫爬取

MPA 按需加载,首次白屏时间不长,访问哪个页面加载哪个页面的资源,能做 SEO,但是页面与页面之间跳转的时候有白屏时间,不太流畅,页面之间的状态不容易保存。

解决方案

底层建设考虑好了之后,往上在去做各种业务形态的解决方案的时候就要好做一些了,基础的基调已经定下来了。

前端在建立解决方案的时候更多的是考虑端,我们的业务分为五端,每端根据当前的业务形态建立对应的技术解决方案。技术栈 + 工程化 + 端的调整。

在每个端的解决方案构建的过程中,逐渐将工程化也统一起来,相同的目录,组件化拆分方式,引入其他模块的方式等等,这些的统一进一步的降低了项目间的维护成本和学习成本,也降低了团队成员离职所带来的风险。

端的调整,我认为不同的端会有不同用户习惯和业务侧重。不同的端 PC、自助机、web,这些会有不同的用户习惯,现在 PC 上处理的大部分是重信息、多交互的功能,自助机上处理的是服务落地、硬件交互、简介信息的处理,web 是移动端便捷操作,内容适中,精致的交互。这几大类我认为是不同端的用户习惯不同。

之前有人问过我同是移动端为什么说用户习惯不一样,我觉得同是移动端用户的交互习惯可以差不多,但是业务侧重点会有不同。由于现在的寡头 APP 效应其实 web 的流量占得很小,我觉得 web 比较类似流量小一些的微信公众号的业务侧重,微信公众号我们期望快速拉新,简要服务提供。小程序我们期望高质量的服务承接和落地。APP 我们期望用户留存,提供业务服务的闭环。

也就是说不同的端,我们可能需要采用不同的解决方案去承接不同的业务。

技术架构

以上是根据当时的业务情况构建的各端技术解决方案,更深入的工程化搭建还需要具体分析业务,但是这里就不进行深入了。每端的解决方案都能单独说很多,这里只是笼统的讲了一下技术架构应该怎么去做,上面每端的方案仅仅是技术方案,不能称为架构。从全局出发才能看到整个团队的技术架构。

团队架构

在我的架构认知中,我觉得团队是根本,固根才能培本,团队不同的阶段是需要不同的方法去带的。

目前了解两种团队架构,资源型:将前端业务全部收拢,然后统一调配分配给现阶段空闲且最适应当前业务的同学,这种架构一般是业务数多于现有资源人数,主要适合初创阶段,这个时候多数业务都是属于探索型,可能不会经常维护,每个业务都投入相关的人力会造成人员浪费,最好将资源收拢统一调配。

业务型:根据现有稳定的大业务,分配不同的同学一直跟随业务迭代,在这个阶段一般属于业务已经稳定,不断的在投入资源开发,在大公司情况比较多,这个时候需要有同学对业务有一定的了解,提升开发效率和提高业务认知程度。

在某些阶段我们也需要混合使用。已经成型并且不断有业务需求的业务采用业务型,留出一些资源作为资源型支持一些探索型的业务。

不同团队阶段

团队在三五人阶段的时候,这个阶段主要的是考虑团队对于业务的输出,并且人员较少,可以采用人盯人的方式,这个阶段自身能力其实和团队成员差距并不大,负责的相关业务也会相对较少,所以现在这个阶段还不需要考虑团队架构,事事都过自己的手是很有必要的,比如需求最好都是自己亲自带着做,代码一定要做 review,一方面会巩固自己的能力,另一方面这个阶段对外输出的质量和效率尤为重要,所以在这个阶段要做好,就需要有细致的把控能力。

团队在十人左右的时候,这个时候就得需要考虑如何最大化的输出团队的能力,需要对现有团队进行划分,这个时候对每个成员都要有一定的了解,明确了解到每个团队成员的优势和劣势,然后将团队成员进行划分,这个时候应该考虑一下团队架构,在这个阶段如果是在小公司一个完整的 team 建议采用资源型,在大公司一般负责一两条业务线采用业务型更好,这个时候就需要注意一些团队划分,将每个人放在合适的位置上,让他们发挥出自己最大的价值从而让整个团队发挥出最大的价值,更多的是执行明确的目标,达成明确的目标,团队更多的是输出。

团队扩张到 20-50 人时,这个阶段我还没有能力经历,我构想一下这个阶段的能力,首先需要认知能力读懂一些决策带来的行业影响,并能有足够的技术积累应对决策所带来的变化。团队层面已经能运用一些方法论对业务构建进行合理化的拆分和协作,将这个关系能很好的和团队映射起来,并且将核心内容进行合理的拆解,到不同的 TL 手上,不一定要按照业务拆分,在这个层面很多时候达成的目标已经不像小团队的时候那么细致了,这个时候可能有很多潜在的需求是需要领导者挖掘的,并且可能已经不能通过直接的划分达到目的,很可能需要一些和现在业务没有什么关系的团队去完成,比如组件、效率工具、底层架构、技术调研这些也需要单独的组去承担,所以这个阶段更多的思考如何合理的拆解目标达成目标,这个时候更多是思考定位,团队的定位,业务的定位,然后合理的拆解并通过一些方法论去管理得到最终的输出,更多的能力输出在于拆解和对最终结果的关注。

上一百人的团队就是我意淫了,更多的是看自己的认知力对于行业的理解,已经统一团队的思想,制定目标团队的目标,这个目标更多的时候要一定程度影响行业,主导行业的走向,就不瞎扯了,实在还没有到这个高度。

团队培养

前面已经有所提及对于团队能力的培养这块,团队完成需求的能力和知道为什么要这么去完成完成的能力应该是在五个人的时候,这个时候考虑更多的是输出,细节和质量的把控。

前端技术的全面性和技术深度是十人团队成长过程中的成果,也是一定程度的基础,在这个阶段已经有全面的业务负责了,团队的技术能力一定要覆盖住这个阶段的业务需求水平,这个阶段还应该有一定的意识培养核心团队开发人员,他们需要承担一定上一个阶段的管理能力,领导者在这个阶段也更多的是对于设计的把控,不需要太关注细节了,细节方面更多是需要下面的人去提高的能力。每个人在不同的阶段是需要不同的成长。

团队的稳定性

团队的稳定性在于技术稳定性、业务稳定性、团队稳定性,前两种就不细聊了。团队稳定性我觉得最重要的是成长,薪资的成长和能力的成长,前者能决定的比较少,后者一定要做到,每个人有不同的未来预期,现在的阶段,以及天赋,上述不同的情况也就意味着需要根据每个人的情况做一些计划。

技术上的提升可以做统一的,但核心的同学就要去成长一些管理,一些技术架构的能力。这些是需要根据自己的情况去制定一些团队成长计划,让每个人都有成长,并且有适合自己兴趣点的业务做,就不会考虑太多离职。能力成长了一定要有对应的薪资体现,因为能力成长的证明就是做出了对应的贡献,管理团队一定要树立目标,达成目标,奖惩,来让团队有良性竞争。

一定要有淘汰,这方面可能是不太擅长的,但是团队不同成员有不同的能力输出,要让团队一直保持竞争和成长,环境不能太安逸,也是一个必要的方式。

当各方面都更好的时候团队有自我的成长,并且团队树立了一些公信力之后会不断地吸引更优秀的同学加入加上淘汰,团队会一直保持正向的循环。