• JavaScript的two-sum问题解法


    一个很常见的问题,找出一个数组中和为给定值的两个数的下标。为了简单一般会注明解只有一个之类的。

    最容易想到的方法是循环遍历,这里就不说了。

    在JS中比较优雅的方式是利用JS的对象作为hash的方式:

     1 var twoSum = function(nums, target) {
     2     var hash = {};
     3     var i;
     4     for (var i = 0; i < nums.length; i++ ) {
     5         if (typeof hash[nums[i]] !== "undefined") {
     6             return [i, hash[nums[i]]];
     7         }
     8         hash[target - nums[i]] = i;
     9     }
    10 };

    这里面还可以做一些小的优化,比如把length拿出来,重复使用的 nums[i] 也抽取出来,遍历的顺序反过来等,最后大概弄成这个样子:

    var twoSum2 = function(nums, target) {
        var hash = {};
        var i;
        var tmp;
        for (i = nums.length; i--; ) {
            tmp = nums[i];
            if (typeof hash[tmp] !== "undefined") {
                return [i, hash[tmp]];
            }
            hash[target - tmp] = i;
        }
    };

    不过这些小的优化基本上不影响大局,在leetcode上测试了好几遍,排名总在75%到85%之间浮动。居然连90%都没达到,我对你很失望

    让我再想想……

    哦对了我记得前几天看到过一个优化循环的方法叫做Duff Device来着,不如试试?

    链接在这里 https://en.wikipedia.org/wiki/Duff%27s_device。(怎么插入链接来着?)

    这东西我的大概理解就是就是把for循环稍微展开一下,本来循环80次的行为,改写成执行10次循环,每次循环里执行8次。这样可以省下一点调用for循环的开销。不过这个前提是for循环里的执行代码比较简单。如果耗时主要在每个循环里的话,这招就不太管用了听说。

    于是我照着介绍的例子,把上面的代码改写了一下:

     1 var twoSum1 = function(nums, target) {
     2     var hash = {};
     3     var i = 0;
     4     var startIndex = nums.length % 8;
     5     var iterations = nums.length / 8;
     6     do {
     7        
     8         switch(startIndex) {
     9             case 0: if(typeof hash[nums[i]] !== "undefined") {return [i, hash[nums[i]]]} hash[target - nums[i]] = i; i++;
    10             case 7: if(typeof hash[nums[i]] !== "undefined") {return [i, hash[nums[i]]]} hash[target - nums[i]] = i; i++;
    11             case 6: if(typeof hash[nums[i]] !== "undefined") {return [i, hash[nums[i]]]} hash[target - nums[i]] = i; i++;
    12             case 5: if(typeof hash[nums[i]] !== "undefined") {return [i, hash[nums[i]]]} hash[target - nums[i]] = i; i++;
    13             case 4: if(typeof hash[nums[i]] !== "undefined") {return [i, hash[nums[i]]]} hash[target - nums[i]] = i; i++;
    14             case 3: if(typeof hash[nums[i]] !== "undefined") {return [i, hash[nums[i]]]} hash[target - nums[i]] = i; i++;
    15             case 2: if(typeof hash[nums[i]] !== "undefined") {return [i, hash[nums[i]]]} hash[target - nums[i]] = i; i++;
    16             case 1: if(typeof hash[nums[i]] !== "undefined") {return [i, hash[nums[i]]]} hash[target - nums[i]] = i; i++;
    17         }
    18    
    19     } while (--iterations);
    20 
    21 };

    嗯,看上去很厉害的样子。接下来在浏览器里测试一下:

    先构造一个长度为1000的数组,来来来:

    呃……好像没什么差别。把数据加大一点,到100000吧:

    卧槽牛逼啊!赶紧把这个放leetCode上试试!

    ……

    ……

    ……

    然而在leetCode上试了之后,虽然通过了,发现和之前的执行效率并没有多大差别……依然没有到90%

    一定是leetCode上的单测数据太小了!于是自个儿在leetCode上的那个小输入框里粘贴了一个十几万长度的数据,跑了一遍,发现仍然没有什么卵用。

    所以大概是我的测试方法有问题?数据翻了100倍时间居然并没有增加多少,好奇怪。

    好吧,这个方法就暂时搁着。再想想其它歪主意。

    用js自带的forEach做循环试试吧,说不定会比手写的要快点。于是有了下列代码:

    var twoSum = function (nums, target) {
        var hash = {};
        var result = [];
        nums.forEach(function (v, i) {
            if (typeof hash[v] !== "undefined") {
                result[0] = i;
                result[1] = hash[v];
            }
            hash[target - v] = i;
        });
        return result;
    }

    不过这有个问题是,forEach循环没法正常地退出,所以无论怎样都要遍历一下整个数组,不能像前面那样找到正确的就退出了,感觉太好。

    所以只能让它不正常地退出了:

    var twoSum = function (nums, target) {
        var hash = {};
        var result = [];
        try {
          nums.forEach(function (v, i) {
              if (typeof hash[v] !== "undefined") {  
                  result[0] = i;
                  result[1] = hash[v];
                  throw "err";
              }
              hash[target - v] = i;
          });
        } catch (err) {
            return result;
        }
    }

    好的就这样,跑一下试试:

     噢好吧,好歹过了90%……

    想到更快的方法再更………………

      

  • 相关阅读:
    35-高级特性之iterable与iterator
    33-高级特性之generator(1)
    34-高级特性之generator(2)
    32-高级特性之类装饰器
    什么是Autolayout
    屏幕适配
    NSTimer
    分页
    UIScrollView的缩放原理
    设置图片圆角
  • 原文地址:https://www.cnblogs.com/kindofblue/p/6254317.html
Copyright © 2020-2023  润新知