1.题号217. 存在重复元素
遍历,放入散列集中,如果散列集中已经存在,返回true
bool containsDuplicate(vector<int>& nums) {
unordered_set<int> s;
for(int i:nums){
if(s.find(i)!=s.end()){
return true;
}
s.insert(i);
}
return false;
}
2.题号219. 存在重复元素 II
要用nums[i]做key,i做value
找到了并符合条件,返回true
因为从左向右找,当前索引一定是大于哈希表中的,所以直接用i-j判断
需要更新最近的重复元素索引,避免{1,0,1,1} 1,这种情况
bool containsNearbyDuplicate(vector<int>& nums, int k) {
unordered_map<int,int> hash;
int n = nums.size();
for(int i=0;i<n;i++){
unordered_map<int,int>::iterator it=hash.find(nums[i]);
if(it!=hash.end()){
if((i-it->second)<=k){ 找到了,并且符合条件
return true;
}else{
hash[nums[i]]=i; 找到了,但超出了范围,需要更新索引为当前
}
}
hash.insert({nums[i],i}); 放入哈希表
}
return false;
}
3.题号904. 水果成篮
滑动窗口与哈希表的完美结合
用哈希表记录每种水果的个数,很方便直接用fruits[tree[i]]++就可以操作
当水果数量超过两种,就l右移,但不是平移,当对应个数为0时要删除水果种类(如果不删除,即使数量为0也会让size+1),直到篮中水果个数小于二,再继续扩张
int getCount(unordered_map<int, int>& mp) {
int ret = 0;
for (auto it : mp) {
ret += it.second;
}
return ret;
}
int totalFruit(vector<int>& tree) {
unordered_map<int, int> fruits;
int ans = 0,l = 0;
for(int i=0;i<tree.size();i++){
fruits[tree[i]]++;
while(l<tree.size() && fruits.size()>2){
fruits[tree[l]]--;
if(fruits[tree[l]]==0){
fruits.erase(tree[l]);
}
l++;
}
ans=max(ans,getCount(fruits));
}
return ans;
}
4.题号1365. 有多少小于当前数字的数字
用bucket记录每个数字出现次数,索引代表值,元素代表个数
再遍历一遍bucket,让除索引0之外的都+=他之前元素,这样bucket[nums[i]-1]
装的就是,小于他的数总和,避免了for循环的嵌套,妙
vector<int> smallerNumbersThanCurrent(vector<int>& nums) {
vector<int> bucket(101);
vector<int> ans;
for(int i=0;i<nums.size();i++){
bucket[nums[i]]++;
}
for(int i=1;i<101;i++){ 妙啊
bucket[i]+=bucket[i-1];
}
for(int i=0;i<nums.size();i++){
ans.push_back(nums[i]==0 ? 0:bucket[nums[i]-1]);
}
return ans;
}
5.题号1160. 拼写单词
用哈希表记录了单词的每个字符数量,比较单词每个字符数量与可用的比较。
用数组记录,遇到相同count–,也可以,但每次都要回复可用字符数组到初始状态
int countCharacters(vector<string>& words, string chars) {
unordered_map<char,int> hash;
int length=0;
for(char c:chars){
hash[c]++;
}
for(string word:words){
unordered_map<char,int> word_hash;
for(char c:word){
word_hash[c]++;
}
bool flag=true;
for(char c:word){
if(word_hash[c]>hash[c]){
flag=false;
break;
}
}
if(flag){
length+=word.size();
}
}
return length;
}
6.题号884. 两句话中的不常见单词
学会了用istringstream分隔字符串
妙在把两个字符串+“ ”连接在一起,就可以只遍历一次
把每个单词用哈希表计数,最后遍历哈希表把次数为1的单词添加到结果数组
vector<string> uncommonFromSentences(string A, string B) {
unordered_map<string,int> map;
vector<string> ans;
A = A+" "+B;
istringstream ss(A);
string word;
while(ss>>word){
map[word]++;
}
for(auto it:map){
if(it.second==1){
ans.push_back(it.first);
}
}
return ans;
}
7. 题号350. 两个数组的交集 II
记录第一个数组每个数字出现次数,遍历第二个数组,相同次数–,加到答案数组,到次数为0时,证明这个数字用完了,删除数字,最后返回答案数组即可
vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
unordered_map<int,int> map;
vector<int> ans;
for(int i:nums1){
map[i]++;
}
for(int i:nums2){
if(map.count(i)){
ans.push_back(i);
map[i]--;
}
if(map[i]==0){
map.erase(i);
}
}
return ans;
}
8. 题号599. 两个列表的最小索引总和
题意是有索引和相同的都加入到数组中
用哈希储存索引,与当前元素索引相加
vector<string> findRestaurant(vector<string>& list1, vector<string>& list2) {
unordered_map<string,int> map;
vector<string> ans;
int n=list1.size(),m=list2.size(),min=INT_MAX;
for(int i=0;i<n;i++){
map[list1[i]]=i;
}
for(int i=0;i<m;i++){
int sum=INT_MAX;
auto it=map.find(list2[i]);
if(it!=map.end()){
sum=i+it->second;
}
if(sum==min){ 这个要先判断,写在后面,会重复插入
ans.push_back(list2[i]);
}
if(sum<min){
min=sum;
ans.clear();
ans.push_back(list2[i]);
}
}
return ans;
}
9. 1711. 大餐计数
高级版两数之和,由于每个数小于等于2的20次方,所以两数之和小于等于2的21磁钢,所以我们只需要判断有没有另一个数存在,让两数之和等于2的0到21次方
如果ans不想用long long可以写为
ans = (ans + map[dif])% mod;
int countPairs(vector<int>& deliciousness) {
unordered_map<int,int> map;
long long ans = 0;
int mod = 1e9 + 7;
for(int i = 0; i < deliciousness.size(); i++){
for(int j = 0; j <= 21; j++){
int dif = (1 << j) - deliciousness[i];
if(dif < 0) continue;
if(map.count(dif)){
ans += map[dif];
}
}
map[deliciousness[i]]++;
}
return ans % mod;
}
10. 697. 数组的度
最短子数组,度还一样,意味着就是,长度为最多的元素第一次出现到最后一次出现的距离
用一个哈希表,键为值,value用pair记录,first为出现次数,second为第一次出现索引,用于记录最短长度
当出现次数与最多次持平时,记录二者最短值,当出现次数大于最大值时,更新答案为他的长度
int findShortestSubArray(vector<int>& nums) {
unordered_map<int,pair<int,int>> map;
int maxCount = 0,minLength = 1;
for(int i = 0; i < nums.size(); i++){
map[nums[i]].first++;
if(map[nums[i]].first == 1){
map[nums[i]].second = i;
}
if(maxCount == map[nums[i]].first){
minLength = min(minLength,i-map[nums[i]].second+1);
}
else if(maxCount < map[nums[i]].first){
maxCount = map[nums[i]].first;
minLength = i - map[nums[i]].second + 1;
}
}
return minLength;
}
11. 面试题 17.11. 单词距离
方法一:
用哈希表记录下单词所有索引位置,与另一个所有单词索引做差取最小值
因为题目要多次比较,所以用哈希表比较好
int findClosest(vector<string>& words, string word1, string word2) {
unordered_map<string,vector<int>> map;
for(int i = 0; i < words.size(); i++){
if(!map.count(words[i])){
map[words[i]] = {};
}
map[words[i]].push_back(i);
}
int ans = INT_MAX;
for(int i = 0; i < map[word1].size(); i++){
for(int j = 0; j < map[word2].size(); j++){
int dif = abs(map[word1][i] - map[word2][j]);
ans = min(ans,dif);
}
}
return ans;
}
方法二:
只需比较相邻的两个要求单词索引,最小值一定出现在其中
只需要查找一次时,这种方法更好
int findClosest(vector<string>& words, string word1, string word2) {
int index1 = -1,index2 = -1,ans = INT_MAX;
for(int i = 0; i < words.size(); i++){
if(words[i] == word1){
index1 = i;
}
else if(words[i] == word2){
index2 = i;
}
if(index1 != -1 && index2 != -1){
ans = min(ans,abs(index1 - index2));
}
}
return ans;
}
12. 剑指 Offer 03. 数组中重复的数字
方法一:set
方法二:
数组元素0到n-1,没有重复的时候正好一个萝卜一个坑
正常归位操作,当自己的坑位已经被相同的萝卜占了时,说明他是重复元素,返回即可
当萝卜恰好在自己的坑时,不会返回,因为只有萝卜不在自己坑时才进行判断
int findRepeatNumber(vector<int>& nums) {
for(int i = 0; i < nums.size() ;i++){
while(nums[i] != i){
if(nums[nums[i]] == nums[i]){
return nums[i];
}
swap(nums[i],nums[nums[i]]);
}
}
return -1;
}
收获与体会
- 要查找的值作为键,因为是根据键查找
- 可以用哈希表记录数组中每个元素出现的个数
- 只要循环中有i++,while条件就不要忘了加 i<n 的类似条件
- 不需要记录次数用set,需要记录次数用map
- min要更新为INT_MAX