其他分享
首页 > 其他分享> > LeetCode 热题100 1.两数之和 解题思路 哈希表知识点回顾(5种方法)

LeetCode 热题100 1.两数之和 解题思路 哈希表知识点回顾(5种方法)

作者:互联网

题干

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

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

 

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]
示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]
 

提示:

2 <= nums.length <= 103
-109 <= nums[i] <= 109
-109 <= target <= 109
只会存在一个有效答案

错误答案:

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) {
    for(var i=0;i<nums.length;i++){
        let neededNum = target - nums[i];
        let neededIndex = nums.indexOf(neededNum);
        if(neededIndex != null && i!=neededIndex){
            return [i,neededIndex]
        }
    }
};

输入:

[3,2,3] 6

输出:

[1,-1]

预期结果:

[0,2]

反思:

完全没有考虑会出现重复数字的情况,而且我这个1,-1的结果怎么出来的呢?查了一下,Js里面的indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置,如果没有找到匹配的字符串则返回 -1。那么indexOf()达不到我们的需求了,还是不能偷懒只能自己手写了。

暴力解法(正解):

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

LeetCode跑出来的数据:

执行用时:92 ms, 在所有 JavaScript 提交中击败了32.64%的用户

内存消耗:37.9 MB, 在所有 JavaScript 提交中击败了60.27%的用户

时间复杂度: O(n^2)

反思2:

这个方法可以说真的是超级无敌暴力了,跑出来的数据也不怎么好看…于是思考有什么更好的办法去解决呢。我想到一个比较浪费空间但是有可能会变快的办法,那就是先把它们按照顺序再排一遍,这样再找的话,其实就不用再穷尽选项了…

画蛇添足想太多型新方案:

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) {
    //排序
    var newArray = new Array();
    nums.map((num,index) => {
        newArray.push([num,index]);
        newArray.sort((a,b) => { return a[0] - b[0]});
    });
    for(var i=0;i<nums.length;i++){
        let neededNum = target - newArray[i][0];

        for(var j=0;j<nums.length;j++){
            if(newArray[j][0] > neededNum){
                break;
            }else if(newArray[j][0] == neededNum){
                if(newArray[j][1] != newArray[i][1]){
                    return [newArray[i][1],newArray[j][1]];
                }else{
                    if(newArray[j+1][0] == newArray[j][0]){
                        return [newArray[i][1],newArray[j+1][1]];
                    }
                    break;
                }
            }
        }
    }
};

LeetCode跑出来的数据:

执行用时:208 ms, 在所有 JavaScript 提交中击败了5.12%的用户

内存消耗:43.6 MB, 在所有 JavaScript 提交中击败了5.00%的用户

反思3:

似乎排序过的数据查找省的那点时间,还没有排序本身耗时多,是我太久没碰算法了,于是看了大佬们的解答…说要用哈希表,然而什么是哈希表我都快忘记的一干二净。刚好现在就来复习一下好了。

什么是哈希表?

 

哈希表是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。

在js当中可以通过存成类似{"3":0,"2":1,"3":2}的形式,在用neededNum in hashMap判断是否在哈希表中,但是仍然存在前面说的类似indexOf的重复数字只能查到第一项的问题,于是有个比较tricky的办法,每次遍历一项往哈希表中插入一项。这样每次后面的数据只能往前面的表内查找,这样可以避免重复的问题。同理我之前的indexOf的代码也可以这么修改。

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target)  {
      let hashMap = {}
      for (let i = 0; i < nums.length; i++) {
        let neededNum = target - nums[i];//获取需要匹配的
        if (neededNum in hashMap) {//需要匹配的在hashMap里面有没有,有的话返回
          return [i, hashMap[neededNum]]
        }
        hashMap[nums[i]] = i/*无论有没有都插入到hashMap,这样的话就是永远是往前面的找,所以也不会重复,因为在找的时候本身是没有被加入到hashMap当中去的*/
      }
    };

LeetCode跑出来的数据:

执行用时:92 ms, 在所有 JavaScript 提交中击败了32.64%的用户

内存消耗:38 MB, 在所有 JavaScript 提交中击败了48.21%的用户

时间复杂度: O(n)
空间复杂度: O(n)

使用indexOf的办法:

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target)  {
   let beforeNums = []; 
   for(var i=0;i<nums.length;i++){
       let neededNum = target - nums[i];
       let neededIndex = beforeNums.indexOf(neededNum);
       if(neededIndex >-1){
           return [i,neededIndex]
       }
       beforeNums.push(nums[i]);
   }
};

相同思路使用Map()的办法:

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target)  {
      var map = new Map()
      for (let i = 0; i < nums.length; i++) {
        let neededNum = target - nums[i];
        const getTargetValue = map.get(neededNum)//有的话返回下标,没有的话返回undefined
        if (!isNaN(getTargetValue)) {
          return [i, getTargetValue]
        }
        map.set(nums[i], i)
      }
    }

这三种方法时间和内存消耗上基本都没有区别

标签:知识点,return,target,nums,number,param,var,热题,两数
来源: https://blog.csdn.net/qq_43106115/article/details/116429776