雜湊表
- 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);
*/
- 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);
}
}
- 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;
}
};
- 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=a\cup b\),\(b=a\cap b\).
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)
返回集合中是否具有該元素。
- 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;
}
};
- 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;
}
};
- 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;
}
}
};
- 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];
}
};
- 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;
}
};
- 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;
}
};
- 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;
}
};
- 550.和為K的子陣列
字首和+雜湊表
我們遍歷陣列,利用雜湊表記錄下字首和的值和相同字首和值出現的次數。
我們的目的是為了\(pre[j:i]=pre[i]-pre[j-1]=k\).則我們只需要記錄下符合\(pre[j-1]=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;
}
};
- 523.連續的子陣列和
字首和+雜湊。這裡要注意採用的存取方式是字首和mod k
的餘數。
為什麼?
有:
因此當出現間隔超過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;
}
};
- 525.連續陣列
題解
注意unordered_map(map)和unordered_set(set)的單一性。一個key只能對應一個value。我們將\(nums[i]=0\to nums[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;
}
};