leetcode中hash表相关题目

面试题 01.04. 回文排列
给定一个字符串,编写一个函数判定其是否为某个回文串的排列之一。

回文串是指正反两个方向都一样的单词或短语。排列是指字母的重新排列。

回文串不一定是字典当中的单词。

示例1:

输入:“tactcoa” 输出:true(排列有"tacocat"、“atcocta”,等等)

解题思路

回文序列:正着读和倒着读都是一样的,也就是正着和倒着的字符是一样的,除了回文中心外,其他字符是成对出现的,只有中间的回文中心是单的。而回文中心的数量是1个。

也就是:正常的回文只有一个字符的数量是奇数,其余的字符都是偶数,所以,只需要判断字符串中字符个数是奇数的数量有几个,如果多余1个则不是回文,返回false,否则是true

/**
 * @param {string} s
 * @return {boolean}
 */
var canPermutePalindrome = function(s) {
//  使用哈希表
    var mapArr=new Map();
    var charS=s.split("");
    for(var i=0;  i<charS.length; i++){
        if(mapArr.has(charS[i])){
            mapArr.set(charS[i],mapArr.get(charS[i])+1);
        }else{
            mapArr.set(charS[i],1);
        }
    }
    //获取map中数量----判断为奇数的字符数量是否大于1,则返回 false
    var count=0;
    for(var val of mapArr.values()){
        if(val % 2 !=0){
            count++;
        }
    }
    if(count>1){
        return false;
    }
    return true;
};

面试题 10.02. 变位词组
编写一种方法,对字符串数组进行排序,将所有变位词组合在一起。变位词是指字母相同,但排列不同的字符串。
注意:本题相对原题稍作修改
示例:
输入: [“eat”, “tea”, “tan”, “ate”, “nat”, “bat”],
输出:
[
[“ate”,“eat”,“tea”],
[“nat”,“tan”],
[“bat”]
]
说明:
所有输入均为小写字母。
不考虑答案输出的顺序。

解题思路:
将字符串数组中的每一个字符串装入字典/哈希表,并判断下一个字符串在字典中是否存在,存在则取出装入结果数组,不存在的加入字典,加入结果数组。
关键在于判断,这些字符串字母是相同的,但是顺序不同,这样的情况与字典中现存的元素该怎么比较?
*解决:*在判断之间,将当前字符串排序,在判断字典中是否存在。
*注意:*最后加入字典和结果数组的是原来的字符串,不是排序后的。

代码

/**
 * @param {string[]} strs
 * @return {string[][]}
 */
var groupAnagrams = function(strs) {
    //定义返回结果集
    var result=[];
    //定义字典map
    var map=new Map();
   
    for(var i=0,j=strs.length; i<j; i++ ){
        var temp=strs[i].split("").sort().join("");
        if(!map.has(temp)){
            map.set(temp,result.length);
            result.push([strs[i]]);
        }
        else{
            result[map.get(temp)].push(strs[i]);           
        }
    }

    return result;
};

面试题 16.02. 单词频率
设计一个方法,找出任意指定单词在一本书中的出现频率。

你的实现应该支持如下操作:

WordsFrequency(book)构造函数,参数为字符串数组构成的一本书
get(word)查询指定单词在书中出现的频率

示例:

WordsFrequency wordsFrequency = new WordsFrequency({“i”, “have”, “an”,
“apple”, “he”, “have”, “a”, “pen”}); wordsFrequency.get(“you”);
//返回0,"you"没有出现过 wordsFrequency.get(“have”); //返回2,"have"出现2次
wordsFrequency.get(“an”); //返回1 wordsFrequency.get(“apple”); //返回1
wordsFrequency.get(“pen”); //返回1

提示:

book[i]中只包含小写字母
1 <= book.length <= 100000
1 <= book[i].length <= 10
get函数的调用次数不会超过100000

解题思路

1、hash法

统计单词出现在书中的次数,想到字典map,书是一个字符串数组,只需要遍历数组将单词存储到字典中,字典中原来存在,则直接+1,不存在则置为1,然后在get函数中,返回字典中word出现的次数。

代码

/**
 * @param {string[]} book
 */
var WordsFrequency = function(book) {
    this.map=new Map();
    for(var word of book){
        if(this.map.has(word)){
            this.map.set(word,this.map.get(word)+1);
        }else{
            this.map.set(word,1);
        }
    }
};

/** 
 * @param {string} word
 * @return {number}
 */
WordsFrequency.prototype.get = function(word) {
    return this.map.get(word) || 0;
};

/**
 * Your WordsFrequency object will be instantiated and called as such:
 * var obj = new WordsFrequency(book)
 * var param_1 = obj.get(word)
 */

2、数组法
与hash法所占用内存就差一点点,但是在时间消耗上数组法所用时间太多

/**
 * @param {string[]} book
 */
var WordsFrequency = function(book) {   
    //数组实现
    this.bookArr=[];
    for(var word of book){
        this.bookArr.push(word);
    }
};

/** 
 * @param {string} word
 * @return {number}
 */
WordsFrequency.prototype.get = function(word) {   
    var num=0;
    for(var i=0,j=this.bookArr.length; i<j; i++){
        if(this.bookArr[i]==word){
            num++;
        }
    }
    return num;
};

/**
 * Your WordsFrequency object will be instantiated and called as such:
 * var obj = new WordsFrequency(book)
 * var param_1 = obj.get(word)
 */

面试题 16.14. 最佳直线
给定一个二维平面及平面上的 N
个点列表Points,其中第i个点的坐标为Points[i]=[Xi,Yi]。请找出一条直线,其通过的点的数目最多。

设穿过最多点的直线所穿过的全部点编号从小到大排序的列表为S,你仅需返回[S[0],S[1]]作为答案,若有多条直线穿过了相同数量的点,则选择S[0]值较小的直线返回,S[0]相同则选择S[1]值较小的直线返回。

示例:

输入: [[0,0],[1,1],[1,0],[2,0]] 输出: [0,2] 解释: 所求直线穿过的3个点的编号为[0,2,3]

提示:

2 <= len(Points) <= 300
len(Points[i]) = 2

解题思路

参考大佬的思路:但有一点我没想明白,多条直线穿过点数量相同,返回下标最小点,下面的代码中是怎么实现的。
两点确定一条直线,因此直接让直线通过两点,count=2,然后循环判断该直线是否穿过ppoints中的其他点,如果穿过,则count++,最后更新res结果数组,返回直线穿过的前两个节点。
其中那个长的if语句用到了三点共线的,其实就是数学推导的过程。
方法1:普通的for循环嵌套

/**
 * @param {number[][]} points
 * @return {number[]}
 */
var bestLine = function (points) {
    var max=0;
    var res=[];
    var l=points.length;
    for(var i=0; i<l; i++){
        for(var j=i+1; j<l; j++){
            var count=2;
            for(var k=0; k<l; k++){
                if(k==i || k==j){ continue;}
                if(
                        (points[j][0]-points[i][0])*(points[k][1]-points[j][1])==
                        (points[j][1]-points[i][1])*(points[k][0]-points[j][0])                    
                    ){
                        count++;
                }
            }
            if(count>max){
                max=count;
                res=[i,j];
            }
        }
    }
    return res;
}

方法2:hash表
关于哈希这个解法,我并没有仔细看,需要心静下来在仔细看。但是这个方法我个人觉得要比上面的更严谨。

/**
 * @param {number[][]} points
 * @return {number[]}
 */
var bestLine = function(points) {
    var inLine = function (p0, p1, p2) {
        return (p1[1] - p0[1]) * (p2[0] - p0[0]) == (p1[0] - p0[0]) * (p2[1] - p0[1])
    }

    var currentDiff, currentAppend, tempDiff, diffArr = [], diffObj = {}, max = [0, '']
    for (var i = 0; i < points.length - 1; i++) {
        for (var k = i + 1; k < points.length; k++) {
            var key = i + ',' + k
            for (var j = k + 1; j < points.length; j++) {
                if (inLine(points[i], points[k], points[j])) {
                    diffObj[key] = diffObj[key] || [i, k]
                    if (diffObj[key].indexOf(j) == -1) {
                        diffObj[key].push(j)
                        diffObj[key] = diffObj[key].sort((a, b) => a - b)
                        if (diffObj[key].length == max[0]) {
                            if (diffObj[key][0] < diffObj[max[1]][0] || (diffObj[key][0] == diffObj[max[1]][0] && diffObj[key][1] < diffObj[max[1]][1])) {
                                max[0] = diffObj[key].length
                                max[1] = key
                            }
                        } else if (diffObj[key].length > max[0]) {
                            max[0] = diffObj[key].length
                            max[1] = key
                        }
                    }
                }
            }
        }
    }
    var res = diffObj[max[1]]
    if (res && res.length > 2) {
        return res.slice(0, 2)
    } else {
        return [0, 1]
    }
};

面试题 16.24. 数对和

设计一个算法,找出数组中两数之和为指定值的所有整数对。一个数只能属于一个数对。

示例 1:

输入: nums = [5,6,5], target = 11 输出: [[5,6]]

示例 2:

输入: nums = [5,6,5,6], target = 11 输出: [[5,6],[5,6]]

提示:
nums.length <= 100000

方法1:排序+双指针
先排序,然后使用双指针,前面一个后面一个,循环判断加和是否等于target,相等则加入结果数组中,大于则r–,小于则l++
注意:在数组的排序中,如果排序元素都是数字,直接使用sortsort函数会出错(因为是按照unicode码排的)),因此需要使用函数重新排。这个方法耗费时间和内存都比较大。

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[][]}
 */
var pairSums = function(nums, target) {
   //排序和双指针
    nums.sort((a,b)=> a-b);
    var res=[];
    var l=0;
    var r=nums.length-1;
    while(l<r){
        if(nums[l]+nums[r]==target){
            var temp=[];
            temp.push(nums[l]);
            temp.push(nums[r]);
            res.push(temp);
            l++;
            r--;
        }else if(nums[l]+nums[r]>target){
            r--;
        }else l++;
    }
    return res;
};

关于sort排序对数组中元素都是纯数字的情况,如果直接使用sort()排序,会出错,因为isor函数使用unicode编码,解决方法,就是重写sort函数:
nums.sort(
(a,b)=> a-b
);
使用的箭头函数;

方法2:hash表法

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[][]}
 */
var pairSums = function(nums, target) { 
     var len=nums.length;
    var res=[];
    var map=new Map();
    for(var i=0; i<len; i++){
            if(map.has(target-nums[i])){
                res.push([nums[i],target-nums[i]]);
                map.set(target-nums[i],map.get(target-nums[i])-1);
                if(map.get(target-nums[i])<1){
                    map.delete(target-nums[i]);
                }
            }else{//将元素都装入map
                if(map.get(nums[i])){
                    map.set(nums[i],map.get(nums[i])+1);
                }else{
                    map.set(nums[i],1);
                }
            }
        }
    return res;
};

这个写法是复制其他大佬的:

 * @param {number[]} nums
 * @param {number} target
 * @return {number[][]}
 */
var pairSums = function (nums, target) {
  const map = new Map()
  for (const num of nums) {
    if (!map.has(num)) {
      map.set(num, 0)
    }
    map.set(num, map.get(num) + 1)
  }

  const res = []
  for (const [key, val] of map) {
    if (map.has(target - key)) {
      let count = Math.min(val, map.get(target - key))
      if (key === target - key) {//num和target-num相等时
        count = val >> 1
      }
      while (count) {
        res.push([key, target - key])
        count--
      }
      map.delete(key)
    }
  }
  return res
}

不定时补充~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值