1、关于leetcode


这是第一篇关于leetcode的题解,就先扯点关于leetcode的话。

其实很早前就在博客园看到过leetcode一些题解,总以为跟一般OJ大同小异,直到最近点开了一篇博文Leetcode 编程训练,无意间点进leetcode的主页看了下,乖乖,居然能用JavaScript提交代码(还能用python、ruby等)!瞬间来了兴趣,一口气把几十道水题都切完了,代码放在了github,有兴趣的可以参考或者帮忙review一下。对于个人认为有意思的题目,楼主也会时不时地写些题解和大家分享下。

2、解题过程


今天要说的是Rotate Array这题,并不是说这题有多么地难(leetcode把它难度定位为EASY),而是让我理解了JavaScript中以前听说过但是一直没引起重视的一个很重要的性质。

先回到这道题本身,题目很简单,给一个数组,向右移动k位,求新的数组,关键来了,题目要求你Do not return anything, modify nums in-place instead.。右移k位,相当于把数组最右边的k位放到数组开头,还要考虑k大于数组长度的情况,似乎也很容易想到,写下如下代码:

var rotate = function(nums, k) {
  k %= nums.length;
  var tmp = [];
  if (k)
    tmp = nums.slice(-k);
  nums.splice(-k, k);

  nums = tmp.concat(nums);
};

tmp保存了右边要移动要前面的数组(slice),而nums自己则截掉后面要移动的数组(splice),然后把两段数组一拼,不是说直接修改nums数组么,那把结果直接赋给nums就ok了!但是无情地返回了wrong answer,leetcode不提供sample,但是出错了会给一组出错了的数据,数据如下:

Input:  [1,2], 1
Output:  [1]
Expected:  [2,1]

尝试着把数组带入,打印结果:

var rotate = function(nums, k) {
  k %= nums.length;
  var tmp = [];
  if (k)
    tmp = nums.slice(-k);
  nums.splice(-k, k);

  nums = tmp.concat(nums);
  console.log(nums); // [2, 1]
};

rotate([1, 2], 1);

靠,输出的真的是你expected的东西啊!正当我百思不得其解的时候,我突然意识到我犯了一个很严重的错误。leetcode服务器匹配你的结果正确与否,不可能进入你写的函数里去判断!而实际上,它应该是这样判断的:

var rotate = function(nums, k) {
  k %= nums.length;
  var tmp = [];
  if (k)
    tmp = nums.slice(-k);
  nums.splice(-k, k);

  nums = tmp.concat(nums);
};

var a = [1, 2];
rotate(a, 1);
console.log(a); // [1]

确实与expected的不符!为什么数组a会变成1?怎样才能变成expected的答案?我们接下去看。

3、JavaScript函数的参数传递方式


关于变量值的复制我们都已经很清楚了,基本类型(undefined、null、boolean、number、string)和引用类型(object)是不一样的。如果从一个变量向另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后把该值赋值到为新变量分配的位置上,此后这两个变量可以参与任何操作而不会互相影响。而当从一个变量向另一个变量复制引用类型的值时,同样也会将存储在变量对象中的值复制一份放到为新变量分配的空间中,不同的是,这个值的副本实际上是一个指针,而这个指针指向存储在堆中的一个对象。复制操作结束后,两个变量实际上将引用同一个对象。因此,改变其中一个变量,就会影响另一个变量:

var a = [0, 1, 2, 3];
var b = a;
b.push(4, 5, 6);
console.log(a); // [0, 1, 2, 3, 4, 5, 6]

而ECMAScript中所有函数的参数都是按值传递的,也就是说,把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样!弄清楚了这一点,我们再回到上面的代码。

当没有执行nums = tmp.concat(nums);这行代码时,数组a和函数rotate的参数nums都引用着同一个地址,于是它们的值一起改变;当执行这行代码后,nums指向了一个新的地址,无论它指向哪里,此时的它已经和数组a没有任何关系,也就是说a的值在这一刻之后不会再变化了。

于是我们很清楚地知道,要想在nums上体现a的变化,函数内的nums参数不能去引用一个新的对象,只能在自身上操作,思考下写下如下代码,终于AC:

var rotate = function(nums, k) {
  k %= nums.length;
  var tmp = [];
  if (k)
    tmp = nums.slice(-k);
  nums.splice(-k, k);

  Array.prototype.unshift.apply(nums, tmp);
};

利用JavaScript的这个性质,能出现一些很神奇的效果,这些我也在做题过程中逐渐体会到了一点,具体题目到时再跟大家分享吧!

理解JavaScript中的参数传递 - leetcode189. Rotate Array的更多相关文章

  1. 深入理解JavaScript中创建对象模式的演变(原型)

    深入理解JavaScript中创建对象模式的演变(原型) 创建对象的模式多种多样,但是各种模式又有怎样的利弊呢?有没有一种最为完美的模式呢?下面我将就以下几个方面来分析创建对象的几种模式: Objec ...

  2. 理解javascript中的回调函数(callback)【转】

    在JavaScrip中,function是内置的类对象,也就是说它是一种类型的对象,可以和其它String.Array.Number.Object类的对象一样用于内置对象的管理.因为function实 ...

  3. 【拾遗】理解Javascript中的Arguments

    前言 最近在看JavaScript相关的知识点,看到了老外的一本Javascript For Web Developers,遇到了一个知识盲点,觉得老外写的很明白很透彻,记录下来加深印象,下面是我摘出 ...

  4. 深入理解JavaScript中的作用域和上下文

    介绍 JavaScript中有一个被称为作用域(Scope)的特性.虽然对于许多新手开发者来说,作用域的概念并不是很容易理解,我会尽我所能用最简单的方式来解释作用域.理解作用域将使你的代码脱颖而出,减 ...

  5. 理解JavaScript中的原型继承(2)

    两年前在我学习JavaScript的时候我就写过两篇关于原型继承的博客: 理解JavaScript中原型继承 JavaScript中的原型继承 这两篇博客讲的都是原型的使用,其中一篇还有我学习时的错误 ...

  6. 深入理解JavaScript中的属性和特性

    深入理解JavaScript中的属性和特性 JavaScript中属性和特性是完全不同的两个概念,这里我将根据自己所学,来深入理解JavaScript中的属性和特性. 主要内容如下: 理解JavaSc ...

  7. 深入理解javascript中执行环境(作用域)与作用域链

    深入理解javascript中执行环境(作用域)与作用域链 相信很多初学者对与javascript中的执行环境与作用域链不能很好的理解,这里,我会按照自己的理解同大家一起分享. 一般情况下,我们把执行 ...

  8. 【干货理解】理解javascript中实现MVC的原理

    理解javascript中的MVC MVC模式是软件工程中一种软件架构模式,一般把软件模式分为三部分,模型(Model)+视图(View)+控制器(Controller); 模型:模型用于封装与应用程 ...

  9. 理解javascript中的策略模式

    理解javascript中的策略模式 策略模式的定义是:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换. 使用策略模式的优点如下: 优点:1. 策略模式利用组合,委托等技术和思想,有效 ...

随机推荐

  1. Maven详细介绍

    Maven 目录 1 什么是Maven? 2 Maven 的好处 3 获取和安装 3.1 获取 3.2 安装 3.2.1 环境变量的配置 4 设置本地仓库 5 创建简单的Maven实例 5.1 使用骨 ...

  2. 导出HTML

    版权声明:本文为博主原创文章,未经博主允许不得转载.

  3. head,tail

    测试文件headtail 1 L 2 L 3 L 4 L 5 L 6 L 7 L 8 L 9 L 10 L 11 L 12 L 13 L 14 L 15 L 16 L 17 L 18 L 19 L h ...

  4. Android中 int,float,Double,String 互相转换

    1 如何将字串 String 转换成整数 int?  A. 有两个方法: 1). int i = Integer.parseInt([String]); 或 i = Integer.parseInt( ...

  5. 使用数据库sqlite3 C语言实现登陆注册的功能

    //此代码为注册功能 void create_regtable() { int rc; //rc为返回值,判断函数是否执行成功 rc=0函数执行成功,rc !=0函数执行失败 sqlite3 *db; ...

  6. git忽略某些文件

    通常项目中编译过程产生的文件或者编译器生成的临时备份文件,不需要通过Git系统去追逐(track). 在项目顶层工作目录中添加一个叫".gitignore"的文件,来告诉Git系统 ...

  7. JDK环境变量详细讲解

    首先,你应该已经安装了 java 的 JDK 了,笔者安装的是:jdk-8u65-windows-x64 下载地址: http://www.oracle.com/technetwork/java/ja ...

  8. 【CSS】使用CSS选择器(第二部分)

    1. 使用结构性伪类选择器 使用结构性伪类选择器能够根据元素在文档中的位置选择元素.这类选择器都有一个冒号字符前缀(:),例如 :empty .它们可以单独使用,也可以跟其他选择器组合使用,如: p: ...

  9. 原生的强大DOM选择器querySelector

    在传统的 JavaScript 开发中,查找 DOM 往往是开发人员遇到的第一个头疼的问题,原生的 JavaScript 所提供的 DOM 选择方法并不多,仅仅局限于通过 tag, name, id ...

  10. BeJavaGod - 如何正确使用数据字典进行分类统一操作(一)

    先说说什么是数据字典,这个玩意一般不太会解释,举个栗子吧~ 每个系统都会有用户表,性别:男(1)女(0) 另外我们做物流的会涉及到车型:卡车(1),轿车(2),挂车(3) 货物类型:危险品(1),普通 ...