Skip to content

LeetCode(力扣)答案解析(一) #2

@Checkson

Description

@Checkson

1. 两数之和

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。

示例

给定 nums = [2, 7, 11, 15], target = 9

因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

解法一(暴力法)

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function (nums, target) {
    var len = nums.length;
    for (var i = 0; i < len; i++) {
        for (var j = i + 1; j < len; j++) {
            if (nums[i] + nums[j] === target) {
                return [i, j];
            }
        }
    }
    return [];
};

暴力法是纯粹枚举所有组合的和,是否与target的值相等。

解法二(两次哈希遍历)

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function (nums, target) {
    var len = nums.length;
    var hashMap = {};
    for (var i = 0; i < len; i++) {
        // 记录每个数值对应数组的下标
        hashMap[nums[i]] = i;
    }
    for (var i = 0; i < len; i++) {
        // 计算差值
        var res = target - nums[i];
        // 若差值在hashMap存在,且下标不相等(题目要求不相同的数)
        if (hashMap[res] && hashMap[res] !== i) {
            return [i, hashMap[res]];
        }
    }
    return [];
};

两次哈希遍历,利用了对象中的key唯一性,而其值存放该key在nums数组下的下标,方便后面对比和组织返回结果。虽然两次哈希遍历需要额外的存储空间,但比暴力法快了不少,这就是时间和空间上作出的权衡,然而,这种方法并不是最快的方法。

解法三(一次哈希遍历)

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function (nums, target) {
    var len = nums.length;
    var hashMap = {};
    for (var i = 0; i < len; i++) {
        var res = target - nums[i];
        if (hashMap[res] || hashMap[res] !== 0) {  // 兼容下标为0的情况
            return [hashMap[res], i];
        }
        hashMap[nums[i]] = i;
    }
    return [];
};

一次哈希遍历在时间复杂度和两次哈希遍历是一样的,但是一次哈希遍历,少了一次遍历nums数组。这里的思路是:一边构造hashMap,一边用target减去当前遍历到下标为i的nums数组中的数,看差值是否在hashMap中,若存在,则返回结果,若不存在,则继续扫描后面的数。

136. 只出现一次的数字

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:

你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例1:

输入: [2,2,1]
输出: 1

示例2:

输入: [4,1,2,1,2]
输出: 4

解法一(暴力法 + 双重循环)

/**
 * @param {number[]} nums
 * @return {number}
 */
var singleNumber = function(nums) {
    var len = nums.length;
    for (var i = 0; i < len; i++) {
        var isSame = false;
        for (var j = 0; j < len; j++) {
            // 两数下标不同,且值相同
            if (i !== j && nums[i] === nums[j]) {
                // 标志已经找到了相同的数
                isSame = true;
                // 跳出当前循环
                break;
            }
        }
        // 若不存在相同的数
        if (!isSame) {
            return nums[i];
        }
    }
    return null;
};

这个解法真的暴力,既耗时,也违反题目要求(线性时间),但是这个是最直接的做法。

解析二(线性时间 + 借助对象排重)

/**
 * @param {number[]} nums
 * @return {number}
 */
var singleNumber = function(nums) {
    var len = nums.length;
    var obj = {};
    for (var i = 0; i < len; i++) {
        var num = nums[i];
        // 判断该数是否存在键值对,若有其值加1,若没有,初始化其值为1
        obj[num] ? obj[num]++ : obj[num] = 1;
    }
    for (var item in obj) {
        // 若该数只出现一次
        if (obj[item] === 1) {
            return item;
        }
    }
    return null;
};

这个方法,运用了对象的key唯一的特性来区分统计数组中的值出现次数,最后遍历一次对象,匹配出其值为1的key值,这就是答案了。虽然这个方法有着不错的性能,但是还是违反题目中不能运用额外的空间的要求。

解法三(遍历数组逐一异或(^))

/**
 * @param {number[]} nums
 * @return {number}
 */
var singleNumber = function(nums) {
    var len = nums.length;
    var result = 0;
    for (var i = 0; i < len; i++) {
        result = result ^ nums[i];
    }
    return result;
};

What?异或是什么?我是谁?我在哪里?别急,异或其实很好理解。异或,是JavaScript和很多其他高级语言的位运算符,我们把这个运算过程叫做位运算。异或是指两数对应位数的值不同则返回1,若相同则返回0。举个例子1 ^ 1 = 0, 而 1 ^ 0 = 1。利用这个特性,我抓住题意中:数组中只有一个数字出现一次,其它数字均出现两次。那么两个相同的数异或永远等于0,而0与任何数,都等于其数本身。利用这个特性,我们可以逐一遍历数组中的数,然后逐一异或,得到最后的结果,就是只出现一次的数字了。异或详细讲解

231. 2的幂

给定一个整数,编写一个函数来判断它是否是 2 的幂次方。

示例1:

输入:1
输出:true
解释:2的0次方 = 1

示例2:

输入: 16
输出: true
解释: 2的4次方 = 16

示例3:

输入: 218
输出: false

解法一(对数求值 + 求整对比)

/**
 * @param {number} n
 * @return {boolean}
 */
var isPowerOfTwo = function(n) {
    var temp = Math.log2(n);
    return temp === Math.floor(temp);
};

这个方法是纯粹式数学思维解题。

解法二(求与对比)

/**
 * @param {number} n
 * @return {boolean}
 */
var isPowerOfTwo = function(n) {
    return n <= 0 ? false : (n & (n-1)) === 0;
};

这个方法非常巧妙。经过我们分析,2的n次方,例如2、4、8、16 ... 对应的二进制为10100100010000 ... 可以看到,2的n次方对应其二进制数,都是开头1然后后面跟着全是0,那么当这个数减1之后,开头就变成了0,后面跟着全是1。例如2 - 1 = 1 = 01(二进制) 4 - 1 = 3 = 011(二进制)8 - 1 = 7 = 0111(二进制) ... 可以发现,2的n次幂与自身减1相与,那么,结果必定是0,数字1同样适用这个规律(1 & 0 = 0)。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions