<冲刺大厂之算法刷题>Hash表(一)

简介: <冲刺大厂之算法刷题>Hash表

242. 有效的字母异位词

题目描述

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。

注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。

示例 1:

输入: s = "anagram", t = "nagaram"
输出: true

示例 2:

输入: s = "rat", t = "car"
输出: false

思路分析

方法一:hash表


由于字符串中的都是小写字母,所以我们可以用数组来作为Hash表.


将s中的字母出现的种类个数保存到数组arr中.


将数组arr减掉t中字母出现的种类个数


判断数组arr是否都为0,如果都为0,则说明 s和t是字母异位词.


方法二


直接sort排序,然后判断两个字符串是否相等.


参考代码

#include<bits/stdc++.h>
using namespace std;
//Hash表的使用 
bool isAnagram(string s, string t) {
  int arr[30] = {0};
  for(char ch : s){//统计s中的字母以及个数 
    arr[ch-'a']++;
  }
  for(char ch : t) {//用t中的字母和s中的进行抵消.. 
    arr[ch-'a']--;
  }
  for(int i = 0;i < 30;i++) {
    if(arr[i]!=0){
      return false;//s和t中有字符不一样. 
    }
  }
  return true; 
}
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)


ff6ba29b007844a182056336e418c20f.jpg

49. 字母异位词分组

题目描述

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。

字母异位词 是由重新排列源单词的字母得到的一个新单词,所有源单词中的字母都恰好只用一次。

示例 1:

输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
输出: [["bat"],["nat","tan"],["ate","eat","tea"]]


示例 2:

输入: strs = [""]
输出: [[""]]


示例 3:

输入: strs = ["a"]
输出: [["a"]]


思路分析

字母异位词其实就是两个串所使用的的字母以及个数都相同,只是顺序不同.所以如果两个串是字母异位词,则这两个串排序后是相等的.


我们可以使用map来存储解决,因为不要求有序和可重复性,所以我们可以使用 unordered_map

我们依次对字符串数组进行遍历,如果当前字符串排序后在map中出现过,则将其存入该key对应的val数组中.如果没有出现过则创建以该串为键的键值对.

最终遍历Map将结果存放到vector数组中.


参考代码

#include<bits/stdc++.h>
using namespace std;
vector<vector<string>> groupAnagrams(vector<string>& strs) {
  unordered_map<string,vector<string>> M;
  vector<vector<string>> res;
  string s;
  for(int i = 0; i < strs.size(); i++) { //
    s = strs[i];
    sort(s.begin(),s.end());
    M[s].push_back(strs[i]);
  }
  //遍历输出
  for(unordered_map<string,vector<string>>::iterator it = M.begin(); it!=M.end(); it++) {
    res.push_back(it->second);
  }
  return res;
}


438. 找到字符串中所有字母异位词

题目描述

给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。

示例 1:

输入: s = "cbaebabacd", p = "abc"
输出: [0,6]

解释:

起始索引等于 0 的子串是 “cba”, 它是 “abc” 的异位词。

起始索引等于 6 的子串是 “bac”, 它是 “abc” 的异位词。

示例 2:

输入: s = "abab", p = "ab"
输出: [0,1,2]

解释:

起始索引等于 0 的子串是 “ab”, 它是 “ab” 的异位词。

起始索引等于 1 的子串是 “ba”, 它是 “ab” 的异位词。

起始索引等于 2 的子串是 “ab”, 它是 “ab” 的异位词。

思路分析

方法一:滑动窗口法+hash表

由于字符串中的都是小写字母,所以我们可以用数组的下标进行映射.即选择数组类型的hash表; 由于求的是子串的异位词,可以使用一个滑动窗口法:窗口大小固定,从前往后移动,寻找符合的子串.


初始化滑动窗口.也就是对arrS和arrP初始化. arrS中至少含有arrP中的所有元素.

判断初始化后的窗口是否相等,如果相同则将其实下标0加入结果集.

S滑动窗口向后移动寻找满足的子串满足是P的异位词,并将满足的起始下标加入结果集.

返回结果集


参考代码

bool judgeArr(int arr1[],int arr2[]) { //判断两个数组是否相等
  for(int i = 0; i < 30; i++) {
    if(arr1[i]!=arr2[i]) {
      return false;
    }
  }
  return true;
}
//滑动窗口法+hash表
vector<int> findAnagrams(string s, string p) {
  int m = s.size();
  int n = p.size();
  vector<int> res;
  int arrS[30] = {0};
  int arrP[30] = {0};
  if(s.size() <p.size()) {//长度s  < p,则s中不可能存在子串 和p是异位词
    return {};
  }
  //初始化滑动窗口 s的窗口至少含有p中的所有元素
  for(int i = 0; i < n; i++) {
    arrS[s[i]-'a']++;
    arrP[p[i]-'a']++;
  }
  if(judgeArr(arrS,arrP)) {//判断初始化后的窗口是否相等
    res.push_back(0);
  }
  //s滑动窗口移动寻找子串满足是p的异位词   注意:s的窗口大小和p相等
  for(int i = n; i<m; i++) {
    arrS[s[i]-'a']++;
    arrS[s[i-n]-'a']--;
    if(judgeArr(arrS,arrP)) {
      res.push_back(i-n+1);
    }
  }
  return res;
}


383. 赎金信

题目描述

为了不在赎金信中暴露字迹,从杂志上搜索各个需要的字母,组成单词来表达意思。


给你一个赎金信 (ransomNote) 字符串和一个杂志(magazine)字符串,判断 ransomNote 能不能由 magazines 里面的字符构成。


如果可以构成,返回 true ;否则返回 false 。


magazine 中的每个字符只能在 ransomNote 中使用一次。

示例 1:

输入:ransomNote = "a", magazine = "b"
输出:false


示例 2:

输入:ransomNote = "aa", magazine = "ab"
输出:false


示例 3:

输入:ransomNote = "aa", magazine = "aab"
输出:true

思路分析

方法一:hash表


因为是字符,所以可以采用数组的hash表(也可以使用unordered_map的hash表,因为考虑到效率而且还不需要排序)

**注:**使用数组还是使用set/map类型的hash表,要认真考虑.如果元素用数组可以很好的表示并且范围很小,那就考虑数组.比如元素字符… 本题使用数组较为简单


方法二:双指针


先对两个串排序.

i指向ransomNote串,j指向magazine串.然后从前往后遍历俩串,如果相同就都++,如果j对应的字符大于了i对应的字符,说明不满足了. 如果j对应的字符小于i对应的字符,说明magazine中的该字符是满足的而且还多余.

如果i能遍历完ransomNote,则说明能找到符合题意的串.

参考代码

方法一:hash表(数组)

//直接LeetCode242进行改造...  数组做hash表
bool canConstruct(string s, string t) {
  int arr[30] = {0};
  for(char ch : s){//统计s中的字母以及个数 
    arr[ch-'a']++;
  }
  for(char ch : t) {//用t中的字母和s中的进行抵消.. 
    arr[ch-'a']--;
  }
  for(int i = 0;i < 30;i++) {
    if(arr[i]>0){
      return false;//s和t中有字符不一样. 
    }
  }
  return true; 
}
//使用map作为哈希表
bool canConstruct(string s1, string s2) {
  if(s1.size()>s2.size()) {
    return false;
  }
  unordered_map<char,int> sMap;
  for(char c : s2) {
    if(sMap.find(c)==sMap.end()) {
      sMap[c] = 1;
    } else {
      sMap[c]++;
    }
  }
  for(char c : s1) {
    if(sMap.find(c)!=sMap.end()) {
      sMap[c]--;
      if(sMap[c]<0){
        return false;
      }
    }else{
      return false;
    }
  }
  return true;
}


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

方法二:双指针

bool canConstruct(string ransomNote, string magazine) {
  if(ransomNote.size()>magazine.size()) {
    return false;
  }
  sort(ransomNote.begin(),ransomNote.end());
  sort(magazine.begin(),magazine.end());
  //cout<<ransomNote<<"--"<<magazine<<endl;
  int i,j;
  for(i = 0,j = 0; i < ransomNote.size(); i++,j++) {
    if(j>=magazine.size()) {
      return false;
    }
    if(ransomNote[i]==magazine[j]) {
      continue;
    } else if(ransomNote[i]>magazine[j]) {
      i--;
    } else {
      return false;
    }
  }
  return true;
}

349. 两个数组的交集

题目描述

给定两个数组,编写一个函数来计算它们的交集。

示例 1:

输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]

示例 2:

输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]

思路分析

方法一:hash表


由样例可知,最后的结果是要去重的. 我们选择unordered_set类型的hash表:输出结果中的每个元素一定是唯一的, 同时可以不考虑输出结果的顺序

**注:**由于数组中数的返回比较大,所以不建议使用数组来作为hash表.


方法二:暴力法(此法代码省略)


先给nums1和nums2排序.


然后双层循环判断nums1的元素是否出现在了nums2中.


判断过程中要进行去重:nums1中循环的元素和前一个相等则不处理,不相等再去nums2中判断是否存在


将nums1和nums2都存在的元素加入结果集

参考代码

方法一:hash表

#include<bits/stdc++.h>
using namespace std;
//方法一: 先给nums1和nums2排序.然后双层循环判断nums1的元素是否出现在了nums2中.遍历时,如果元素和前一个相等则不处理,不相等再去nums2中判断是否存在.
//方法二:先去重使用nums1_set 然后遍历nums2,判断元素在nums1_set中是否出现,如果出现则加入结果集合..O(N)   unordered_set的查找效率是O(1)
//另外通过这个题,也要学到vector初始化set,set初始化vector的知识点
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
  unordered_set<int> result_set;//结果集
  unordered_set<int> nums1_set(nums1.begin(),nums1.end()) ;
  for(int i = 0; i < nums2.size(); i++) {
    //如果发现 nums2的元素 ,在nums1_set中出现过
    if(nums1_set.find(nums2[i])!=nums1_set.end()) {
      result_set.insert(nums2[i]) ;
    }
  }
  return vector<int>(result_set.begin(),result_set.end());
}
相关文章
|
3月前
|
存储 缓存 负载均衡
一致性 Hash 算法 Hash 环发生偏移怎么解决
一致性 Hash 算法 Hash 环发生偏移怎么解决
115 1
|
3月前
|
机器学习/深度学习 存储 算法
【算法沉淀】刷题笔记:并查集 带权并查集+实战讲解
【算法沉淀】刷题笔记:并查集 带权并查集+实战讲解
|
2月前
|
存储 算法 C语言
【数据结构与算法 刷题系列】合并两个有序链表
【数据结构与算法 刷题系列】合并两个有序链表
|
5天前
【刷题记录】最大公因数,最小公倍数(辗转相除法、欧几里得算法)
【刷题记录】最大公因数,最小公倍数(辗转相除法、欧几里得算法)
|
14天前
|
算法 Python
【Leetcode刷题Python】改进的算法,高效求一个数的因子
一个高效的Python函数用于找出一个整数的所有因子,通过仅遍历到该数平方根的范围来优化性能。
23 0
|
2月前
|
算法 C++
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题-2
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题
|
2月前
|
算法 C++
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题-1
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题
|
2月前
|
算法
【数据结构与算法 刷题系列】求带环链表的入环节点(图文详解)
【数据结构与算法 刷题系列】求带环链表的入环节点(图文详解)
|
2月前
|
算法
【数据结构与算法 刷题系列】判断链表是否有环(图文详解)
【数据结构与算法 刷题系列】判断链表是否有环(图文详解)
|
2月前
|
算法
【数据结构与算法 刷题系列】移除链表元素
【数据结构与算法 刷题系列】移除链表元素

热门文章

最新文章