哈希表

哈希表

  1. 705.设计哈希集合(模版,开链表)

题解

这是开链表(链表形结构)。

typedef struct LinkedList
{
struct LinkedList *next;
int key;
// value,key-value 对。
} ListNode;
typedef struct {
ListNode *head;
} MyHashSet;
const int size=9973;
ListNode *init()
{
ListNode *Node=(ListNode *) malloc(sizeof(ListNode));
Node->key=0;
Node->next=NULL;
return Node;
}
void delete(ListNode *head)
{
if(head==NULL) return;
ListNode *delNode=head,*delNext=delNode->next;
while(delNext!=NULL)
{
free(delNode);
delNode=delNext;
if(delNext!=NULL) delNext=delNext->next;
}
}
// 创建散列表。
MyHashSet *myHashSetCreate()
{
MyHashSet *obj=(MyHashSet *) malloc(sizeof(MyHashSet)*size);
for(int i=0;i<size;i++)
obj[i].head=NULL;
return obj;
}
// 添加元素。
void myHashSetAdd(MyHashSet *obj, int key)
{
int realDst=key % size;
ListNode *storeDst;
// 头节点为空,现行处理头节点。
if(obj[realDst].head==NULL)
{
obj[realDst].head=init();
storeDst=obj[realDst].head;
storeDst->key=key;
return;
}
//头节点非空,移到链表末尾。
storeDst=obj[realDst].head;
if(storeDst->key==key) return;
while(storeDst->next != NULL)
{
storeDst=storeDst->next;
if(storeDst->key==key) return;
}
storeDst->next=init();
storeDst=storeDst->next;
storeDst->key=key;
}
void myHashSetRemove(MyHashSet *obj, int key)
{
int realDst=key % size;
if(obj[realDst].head==NULL) return;
ListNode *removeDst=obj[realDst].head;
// 1. 处理是头节点的情况。
if(removeDst->key == key)
{
obj[realDst].head=removeDst->next;
free(removeDst);
removeDst=NULL;
return;
}
// 2. 处理中间节点。
ListNode *removePre=removeDst;
while(removeDst!=NULL)
{
if(removeDst->key==key)
break;
else
{
removePre=removeDst;
removeDst=removeDst->next;
}
}
if(removeDst==NULL) return; // 没有可以删除的,直接返回。
removePre->next=removeDst->next;
free(removeDst);
removeDst=NULL;
removePre=NULL;
}
// 判断key是否存在。
bool myHashSetContains(MyHashSet *obj, int key)
{
int checkDst=key % size;
if(obj[checkDst].head==NULL) return false;
ListNode *check=obj[checkDst].head;
while(check!=NULL)
{
if(check->key==key) return true;
check=check->next;
}
return false;
}
void myHashSetFree(MyHashSet *obj)
{
for(int i=0;i<size;i++)
{
if(obj[i].head==NULL) continue;
delete(obj[i].head);
}
}
/**
* Your MyHashSet struct will be instantiated and called as such:
* MyHashSet* obj = myHashSetCreate();
* myHashSetAdd(obj, key);
* myHashSetRemove(obj, key);
* bool param_3 = myHashSetContains(obj, key);
* myHashSetFree(obj);
*/
  1. 706.设计哈希映射(模版,开链表)

题解

typedef struct LinkedList
{
struct LinkedList *next;
int key;
int value;
} ListNode;
typedef struct {
ListNode *head;
} MyHashMap;
const int size=9973;
ListNode *init()
{
ListNode *Node=(ListNode *) malloc(sizeof(ListNode));
Node->key=0;
Node->next=NULL;
return Node;
}
void delete(ListNode *head)
{
if(head==NULL) return;
ListNode *delNode=head,*delNext=delNode->next;
while(delNext!=NULL)
{
free(delNode);
delNode=delNext;
if(delNext!=NULL) delNext=delNext->next;
}
}
// 创建散列表。
MyHashMap *myHashMapCreate()
{
MyHashMap *obj=(MyHashMap *) malloc(sizeof(MyHashMap)*size);
for(int i=0;i<size;i++)
obj[i].head=NULL;
return obj;
}
// 添加元素。
void myHashMapPut(MyHashMap *obj, int key, int value)
{
int realDst=key % size;
ListNode *storeDst;
// 头节点为空,现行处理头节点。
if(obj[realDst].head==NULL)
{
obj[realDst].head=init();
storeDst=obj[realDst].head;
storeDst->key=key;
storeDst->value=value;
return;
}
//头节点非空,移到链表末尾。
storeDst=obj[realDst].head;
if(storeDst->key==key)
{
storeDst->value=value;
return;
}
while(storeDst->next != NULL)
{
storeDst=storeDst->next;
if(storeDst->key==key)
{
storeDst->value=value;
return;
}
}
storeDst->next=init();
storeDst=storeDst->next;
storeDst->key=key;
storeDst->value=value;
}
void myHashMapRemove(MyHashMap *obj, int key)
{
int realDst=key % size;
if(obj[realDst].head==NULL) return;
ListNode *removeDst=obj[realDst].head;
// 1. 处理是头节点的情况。
if(removeDst->key == key)
{
obj[realDst].head=removeDst->next;
free(removeDst);
removeDst=NULL;
return;
}
// 2. 处理中间节点。
ListNode *removePre=removeDst;
while(removeDst!=NULL)
{
if(removeDst->key==key)
break;
else
{
removePre=removeDst;
removeDst=removeDst->next;
}
}
if(removeDst==NULL) return; // 没有可以删除的,直接返回。
removePre->next=removeDst->next;
free(removeDst);
removeDst=NULL;
removePre=NULL;
}
// 判断key是否存在。
int myHashMapGet(MyHashMap *obj, int key)
{
int checkDst=key % size;
if(obj[checkDst].head==NULL) return -1;
ListNode *check=obj[checkDst].head;
while(check!=NULL)
{
if(check->key==key) return check->value;
check=check->next;
}
return -1;
}
void myHashMapFree(MyHashMap *obj)
{
for(int i=0;i<size;i++)
{
if(obj[i].head==NULL) continue;
delete(obj[i].head);
}
}
  1. 217.存在重复元素

题解

用自己的模版:

const int size=99991; // 哈希表的动态分配大小,最好是质数。
typedef struct LinkedList
{
struct LinkedList *next;
int key;
// value,key-value 对。
} ListNode;
// 定义散列表。散列表存储的是相同映射的头节点。
typedef struct HashSet
{
ListNode *head;
}MyHashSet;
ListNode *init()
{
ListNode *Node=(ListNode *) malloc(sizeof(ListNode));
Node->key=0;
Node->next=NULL;
return Node;
}
void delete(ListNode *head)
{
if(head==NULL) return;
ListNode *delNode=head,*delNext=delNode->next;
while(delNext!=NULL)
{
free(delNode);
delNode=delNext;
if(delNext) delNext=delNext->next;
}
}
// 创建散列表。
MyHashSet *myHashSetCreate()
{
MyHashSet *obj=(MyHashSet *) malloc(sizeof(MyHashSet)*size);
for(int i=0;i<size;i++)
obj[i].head=NULL;
return obj;
}
// 添加元素。
void myHashSetAdd(MyHashSet *obj, int key)
{
int realDst=abs(key % size);
ListNode *storeDst;
// 头节点为空,现行处理头节点。
if(obj[realDst].head==NULL)
{
obj[realDst].head=init();
storeDst=obj[realDst].head;
storeDst->key=key;
return;
}
//头节点非空,移到链表末尾。
storeDst=obj[realDst].head;
if(storeDst->key==key) return;
while(storeDst->next != NULL)
{
if(storeDst->key==key) return;
storeDst=storeDst->next;
}
storeDst->next=init();
storeDst=storeDst->next;
storeDst->key=key;
}
void myHashSetRemove(MyHashSet *obj, int key)
{
int realDst=abs(key % size);
if(obj[realDst].head==NULL) return;
ListNode *removeDst=obj[realDst].head;
// 1. 处理是头节点的情况。
if(removeDst->key == key)
{
obj[realDst].head=removeDst->next;
free(removeDst);
removeDst=NULL;
return;
}
// 2. 处理中间节点。
ListNode *removePre=removeDst;
while(removeDst!=NULL)
{
if(removeDst->key==key)
break;
else
{
removePre=removeDst;
removeDst=removeDst->next;
}
}
if(removeDst==NULL) return; // 没有可以删除的,直接返回。
removePre->next=removeDst->next;
free(removeDst);
removeDst=NULL;
removePre=NULL;
}
// 判断key是否存在。
bool myHashSetContains(MyHashSet *obj, int key)
{
int checkDst=abs(key % size);
if(obj[checkDst].head==NULL) return false;
ListNode *check=obj[checkDst].head;
while(check!=NULL)
{
if(check->key==key) return true;
check=check->next;
}
return false;
}
void myHashSetFree(MyHashSet *obj)
{
for(int i=0;i<size;i++)
{
if(obj[i].head==NULL) continue;
delete(obj[i].head);
}
}
bool containsDuplicate(int* nums, int numsSize) {
MyHashSet *obj=myHashSetCreate();
bool flag=false;
for(int i=0;i<numsSize;i++)
{
flag=myHashSetContains(obj,nums[i]);
if(flag==true) return flag;
myHashSetAdd(obj,nums[i]);
}
return flag;
}

尝试学习C++的STL。

class Solution {
public:
bool containsDuplicate(vector<int>& nums) {
unordered_set<int> s;
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
for(auto x:nums)
{
if(s.find(x)!=s.end())
return true;
s.insert(x);
}
return false;
}
};
  1. 349.两个数组的交集

题解

class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> set1,set2;
for(auto x:nums1)
set1.insert(x);
for(auto x:nums2)
set2.insert(x);
vector<int> ans;
if(set1.size()>set2.size())
{
for(auto &x:set2)
{
if(set1.contains(x))
ans.push_back(x);
}
}
else
{
for(auto &x:set1)
{
if(set2.contains(x))
ans.push_back(x);
}
}
return ans;
}
};

题解二

使用merge函数。
a.merge(b)
a=ab,b=ab.

class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> set1,set2;
for(auto x:nums1)
set1.insert(x);
for(auto x:nums2)
set2.insert(x);
vector<int> ans;
set1.merge(set2);
for(auto &x:set2)
ans.push_back(x);
return ans;
}
};

注释

unordered_set<template> 是哈希无序集合。一般用:
1. find(T)来返回元素所处于的迭代器地址。
2. count(T)来返回含有的指定元素的个数。
3. contains(T)返回集合中是否具有该元素。

  1. 128.最长连续序列

采用有序的set。

class Solution {
public:
int longestConsecutive(vector<int>& nums) {
set<int> s;
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
for(auto i:nums)
{
s.insert(i);
}
int count=0;
int ans=0;
int pre,current;
for (auto &x:s)
{
if(count==0)
{
pre=x;
count++;
continue;
}
current=x;
if(current==pre+1)
{
count++;
pre=current;
continue;
}
else
{
ans=count>ans?count:ans;
pre=current;
count=1;
}
}
ans=count>ans?count:ans;
return ans;
}
};
  1. 290.单词规律

思路

注意采用两个无序映射。需要分别检查pattern与substring的关系,也要检查substring与pattern的关系,检查他们的双射关系。

分隔字符串采用substr(pos,num)函数。寻找下标。分隔为[pos,pos+num).

最后要注意匹配时要将pattern全部使用完成。

题解

class Solution {
public:
bool wordPattern(string pattern, string s) {
unordered_map<char,string> p;
unordered_map<string,char> q;
int i=0,cnt=0,j=0;
string substring;
while(i<s.size())
{
while(s[i+cnt]!=' ')
{
cnt++;
if(i+cnt>=s.size()) break;
}
substring=s.substr(i,cnt);
i=i+cnt+1;
cnt=0;
if(p.find(pattern[j])==p.end())
p.emplace(make_pair(pattern[j],substring));
if(p[pattern[j]]!=substring) return false;
if(q.find(substring)==q.end())
q.emplace(make_pair(substring,pattern[j]));
if(q[substring]!=pattern[j]) return false;
j++;
}
if(j!=pattern.size()) return false;
return true;
}
};
  1. 532.数组中的k-diff数对

思路

采用有序的哈希表进行计数。并且寻找当前数+k是否存在即可。
注意区分讨论k=0.

题解

class Solution {
public:
int findPairs(vector<int>& nums, int k) {
map<int,int> p;
for(auto i:nums)
{
if(p.find(i)==p.end())
p.emplace(make_pair(i,1));
else
p[i]++;
}
int ans=0;
if(k==0)
{
for(auto &[i,cnt]:p)
{
if(cnt>1)
ans++;
}
return ans;
}
else
{
for(auto &[i,cnt]:p)
{
if(p.contains(i+k))
ans++;
}
return ans;
}
}
};
  1. 138.随机链表的复制

思路1

创建两个哈希表,一个将原有node映射到下标,一个将下标映射到现有的node.

/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
Node* copyRandomList(Node* head) {
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
if(!head) return nullptr;
// build 2 hash map.
// hashmap 1: original node -> index;
// hashmap 2: index -> new node;
Node *original_chain=head;
unordered_map<Node*,int> Node2index;
unordered_map<int,Node*> index2Node;
int index=0;
while(original_chain)
{
Node2index.emplace(make_pair(original_chain,index++));
original_chain=original_chain->next;
}
Node2index.emplace(make_pair(nullptr,index));
original_chain=head;
index=0;
Node *new_chain=new Node(original_chain->val);
Node *read_new=new_chain;
index2Node.emplace(make_pair(index++,read_new));
while(original_chain->next)
{
read_new->next=new Node(original_chain->next->val);
index2Node.emplace(make_pair(index++,read_new->next));
original_chain=original_chain->next;
read_new=read_new->next;
}
index2Node.emplace(make_pair(index++,nullptr));
original_chain=head;
read_new=new_chain;
while(original_chain)
{
int ind=Node2index[original_chain->random];
Node *rand=index2Node[ind];
read_new->random=rand;
read_new=read_new->next;
original_chain=original_chain->next;
}
return new_chain;
}
};

思路2(更好)

采用递归+回溯的方法,可以更好地只采用一个哈希映射进行。

class Solution {
public:
unordered_map<Node*, Node*> cachedNode;
Node* copyRandomList(Node* head) {
if (head == nullptr) {
return nullptr;
}
if (!cachedNode.count(head)) {
Node* headNew = new Node(head->val);
cachedNode[head] = headNew;
headNew->next = copyRandomList(head->next);
headNew->random = copyRandomList(head->random);
}
return cachedNode[head];
}
};
  1. 554.砖墙

思路

遍历整个数组。并且将数组中的数字累加得到新的边界标记。利用哈希表存储边界标记并得到最大标记即可。

class Solution {
public:
int leastBricks(vector<vector<int>>& wall) {
int range=0;
for(int i=0;i<wall[0].size();i++)
range+=wall[0][i];
map<int,int> bucket;
int maximum=0;
for(auto &row:wall)
{
int mark=0;
for(auto i:row)
{
mark+=i;
if(mark<range && mark>0)
{
bucket[mark]++;
maximum=max(bucket[mark],maximum);
}
}
}
return wall.size()-maximum;
}
};
  1. 609.在系统中查找重复文件
class Solution {
public:
void cut(string &str,multimap<string,string> &m)
{
string path="";
int i;
for(i=0;i<str.size();i++)
{
if(str[i]==' ') break;
path+=str[i];
}
path+='/';
i++;
string doc="";
string contents="";
for(;i<str.size();i++)
{
if(str[i]=='(')
{
i++;
while(str[i]!=')')
contents+=str[i++];
}
if(str[i]==')')
{
m.emplace(make_pair(contents,path+doc));
continue;
}
if(str[i]==' ')
{
doc="";
contents="";
continue;
}
doc+=str[i];
}
}
vector<vector<string>> findDuplicate(vector<string>& paths) {
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
multimap<string,string> pathWithContents;
for(auto &i:paths)
{
cut(i,pathWithContents);
}
vector<vector<string> > ans;
vector<string> ans_temp;
string content="";
for(auto &[con,doc]:pathWithContents)
{
if(content=="")
{
content=con;
ans_temp.push_back(doc);
}
else if(content==con)
{
ans_temp.push_back(doc);
}
else
{
if(ans_temp.size()>1)
{
ans.push_back(ans_temp);
ans_temp={};
ans_temp.push_back(doc);
content=con;
}
else
{
ans_temp={};
ans_temp.push_back(doc);
content=con;
}
}
}
if(ans_temp.size()>1)
ans.push_back(ans_temp);
return ans;
}
};
  1. 454.四数相加II

思路

分组哈希。

题解

class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map<int,int> p;
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
for (auto i:nums1)
{
for(auto j:nums2)
p[i+j]++;
}
int ans=0;
for(auto k:nums3)
{
for(auto l:nums4)
ans+=p[-k-l];
}
return ans;
}
};
  1. 550.和为K的子数组

前缀和+哈希表

我们遍历数组,利用哈希表记录下前缀和的值和相同前缀和值出现的次数。
我们的目的是为了pre[j:i]=pre[i]pre[j1]=k.则我们只需要记录下符合pre[j1]=pre[i]k的j即可。
这样我们就有

题解

class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
unordered_map<int,int> p;
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
p[0]++;
int temp=0;
int ans=0;
for(auto i:nums)
{
temp+=i;
if(p.find(temp-k)!=p.end())
ans+=p[temp-k];
p[temp]++;
}
return ans;
}
};
  1. 523.连续的子数组和

前缀和+哈希。这里要注意采用的存取方式是前缀和mod k的余数。

为什么?

有:

pre[i]=(pre[i1]+nums[i])modkif nums[i]modk0pre[i]=pre[i1]

因此当出现间隔超过1的余数相同的前缀时,就存在一个区间使得其中的和是k的倍数。

class Solution {
public:
bool checkSubarraySum(vector<int>& nums, int k) {
// prefix sum + hash table.
unordered_map<int,int> p;
int temp=0;
int index=0;
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
p.emplace(make_pair(0,-1));
for(index=0;index<nums.size();index++)
{
temp=(temp+nums[index])%k;
if(p.find(temp)!=p.end() && index-p[temp]>1)
return true;
else
p.emplace(make_pair(temp,index));
}
return false;
}
};
  1. 525.连续数组

题解

注意unordered_map(map)和unordered_set(set)的单一性。一个key只能对应一个value。我们将nums[i]=0nums[i]=1,这样是为了计算前缀中0和1的差值。

只需要保留首次出现sum时的数组索引下标即可。剩下的全部只进行长度的计算。

0 0 1 0 0 0 1 1 1 1 //nums
0 -1 -2 -1 -2 -3 -4 -3 -2 -1 0 //prefix
0 0 2 2 2 2 2 4 6 8 //length
class Solution {
public:
int findMaxLength(vector<int>& nums) {
// prefix sum and hash.
// prefix sum and hash.
unordered_map<int,int> p;
p[0]=-1;
int index=0;
int sum=0;
int ans=0;
for(auto i:nums)
{
// find 1 and -1.
sum+=(i==0)?-1:1;
if(p.find(sum)!=p.end())
{
ans=ans>index-p[sum]?ans:index-p[sum];
index++;
}
else
p.emplace(make_pair(sum,index++));
}
return ans;
}
};
posted @   木木ちゃん  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示