当前位置: 首页 > news >正文

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%……

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

 

  

转载于:https://www.cnblogs.com/kindofblue/p/6254317.html

相关文章:

  • nginx ssl
  • 关于Java内部类的初始化
  • noi 1.5 45:金币
  • nginx location配置
  • ArcGIS Engine 编辑- IWorkspaceEdit
  • Access-Control-Allow-Origin与跨域
  • linux下alsa架构音频驱动播放wav格式文件
  • [转].NET Core配置文件加载与DI注入配置数据
  • Makefile注意点总结
  • 深入浅出Puppet(一)
  • Mirco F-measure and Macro F-measure
  • mac上使用zsh配置环境变量
  • find用法积累
  • c# 静态变量【学习笔记】
  • linux内核栈与用户栈【转】
  • [译]Python中的类属性与实例属性的区别
  • 2017-09-12 前端日报
  • 2018天猫双11|这就是阿里云!不止有新技术,更有温暖的社会力量
  • css属性的继承、初识值、计算值、当前值、应用值
  • Intervention/image 图片处理扩展包的安装和使用
  • Koa2 之文件上传下载
  • learning koa2.x
  • node-glob通配符
  • swift基础之_对象 实例方法 对象方法。
  • 紧急通知:《观止-微软》请在经管柜购买!
  • 配置 PM2 实现代码自动发布
  • 嵌入式文件系统
  • 实现菜单下拉伸展折叠效果demo
  • Android开发者必备:推荐一款助力开发的开源APP
  • Hibernate主键生成策略及选择
  • mysql面试题分组并合并列
  • Prometheus VS InfluxDB
  • ​3ds Max插件CG MAGIC图形板块为您提升线条效率!
  • # Java NIO(一)FileChannel
  • # 透过事物看本质的能力怎么培养?
  • #define用法
  • (Java)【深基9.例1】选举学生会
  • (层次遍历)104. 二叉树的最大深度
  • (第61天)多租户架构(CDB/PDB)
  • (二十一)devops持续集成开发——使用jenkins的Docker Pipeline插件完成docker项目的pipeline流水线发布
  • (附源码)spring boot儿童教育管理系统 毕业设计 281442
  • (附源码)ssm跨平台教学系统 毕业设计 280843
  • (三分钟)速览传统边缘检测算子
  • (十八)用JAVA编写MP3解码器——迷你播放器
  • (转) RFS+AutoItLibrary测试web对话框
  • (转)JVM内存分配 -Xms128m -Xmx512m -XX:PermSize=128m -XX:MaxPermSize=512m
  • (转)机器学习的数学基础(1)--Dirichlet分布
  • (转)如何上传第三方jar包至Maven私服让maven项目可以使用第三方jar包
  • (轉)JSON.stringify 语法实例讲解
  • ***微信公众号支付+微信H5支付+微信扫码支付+小程序支付+APP微信支付解决方案总结...
  • .NET 6 Mysql Canal (CDC 增量同步,捕获变更数据) 案例版
  • .net core 3.0 linux,.NET Core 3.0 的新增功能
  • .NET 回调、接口回调、 委托
  • .NET程序员迈向卓越的必由之路
  • .NET构架之我见