哈希表
1.定義:利用散列技術(建立一個對應關系)將記錄存儲在一塊連續的存儲空間中,這塊連續存儲空間稱為散列表或者哈希表。
2.性質:
- 散列技術即是一種存儲方法,也是一種查找方法。
- 數據元素之間沒有邏輯關系,不能像其他數據結構利用連線圖表示出來。
- 存儲位置和關鍵字相關聯。是一個面向查找的數據存儲結構。
3.設計要求:
- 計算簡單,就是建立對應關系的函數應該簡潔,並能夠防止地址沖突
- 散列表的地址分布均勻。
4.方法選取:
- 方法很多:直接定地址法,平方取中法,數字分析法,除留余數法
- 除留余數法:是最常用的構造散列函數的方法
\[f(key)=key \ mod\ p (p\leq m),m=length(hash map)$$ <br> #### 5.解決散列沖突問題: - 開放定址法:地址一旦沖突,就去尋找下一個空的散列地址。這種方法也叫線性探測法。 $$f_{i}(key)=(f(key)+d_{i}) \ mod\ m\]
- 鏈地址法:一旦出現沖突,原地增加鏈表,增加結點。
- 公共溢出法:將所有的沖突值進行溢出,添加一個溢出表,所有沖突值按順序全部存入溢出表
目錄
- [347.TOP k frequent elements](#347.TOP k frequent elements)
- [349.兩個數組的交集](#349. Intersection of Two Arrays)
- [187.DNA中的重復序列](#187.Repeated DNA Sequences)
- [205.相同結構的字符串](#205.Isomorphic String)
- [451.按照頻率排序字符](#451. Sort Characters By Frequency)
- [500.鍵盤行元素](#500. Keyboard Row)
- [508.子樹和的出現頻率](#508. Most Frequent Subtree Sum)
- [389.字符串的不同](#389. Find the Difference)
- [409.最長的回文字符串](#409. Longest Palindrome)
- [438.找到所有回文字符串的位置](#438. Find All Anagrams in a String)
349. Intersection of Two Arrays
// unordered_set 哈希表的數據結構的使用
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
vector<int> result;
unordered_set<int> nums1_set (nums1.begin(),nums1.end());
for(auto i:nums2){
if(nums1_set.count(i)){
result.push_back(i);
nums1_set.erase(i);
}
}
return result;
}
};
347.TOP k frequent elements
# 用了兩種數據結構,一個字典,一個堆排序
from collections import Counter
import heapq
class Solution:
def topKFrequent(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
nums_dict=Counter(nums)
return heapq.nlargest(k,nums_dict,nums_dict.get)
# 自己寫的,但是不對
from collections import Counter
class Solution:
def topKFrequent(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
nums_dict=Counter(nums)
a=[]
for i,v in nums_dict.items(): # 這里估計是缺少排序
a.append(i)
return a[0:k]
// c++ 是使用 map哈希和優先隊列來實現
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
// 好好學習如何新建一個哈希map
unordered_map<int,int> map;
for(int num : nums){
map[num]++;
}
vector<int> res;
// pair<first, second>: first is frequency, second is number
priority_queue<pair<int,int>> pq;
for(auto it = map.begin(); it != map.end(); it++){
// 實現對pair,關聯容器還是要看看
pq.push(make_pair(it->second, it->first));// 注意這個優先隊列的特性,在
// 插入元素的時候就根據 it->second 的大小值進行排序了。
if(pq.size() > (int)map.size() - k){
res.push_back(pq.top().second);
pq.pop();
}
}
return res;
}
};
// 同樣的方法
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
vector<int> result;
unordered_map<int,int>nums_map;
for(int num:nums){
nums_map[num]++;
}
priority_queue<pair<int,int>> pq;
for(auto it=nums_map.begin(); it!=nums_map.end();it++){
pq.push(make_pair(it->second,it->first));
}
// 這樣會更好的理解一點
for(int j=0;j<k;j++){
result.push_back(pq.top().second);
pq.pop();
}
return result;
}
};
187.Repeated DNA Sequences
// 自己思考的方法,就是最暴力的讀取計數法
// 利用哈希map來存儲個數
class Solution {
public:
vector<string> findRepeatedDnaSequences(string s) {
vector<string> result;
if(s.size()<10)
return result;
map<string,int> string_count;
for(int i=0;i<s.size()-10+1;i++){
string sub_str=s.substr(i,10);
string_count[sub_str]++;
}
for(const auto &w:string_count){
if(w.second>1){
result.push_back(w.first);
}
}
return result;
}
};
205.Isomorphic String
// 這個是沒有ac的版本
// 主要問題是在建立映射的時候出現了一點問題
class Solution {
public:
bool isIsomorphic(string s, string t) {
map<char,char> str_map;
if(s.size()!=t.size())
return false;
for(int i=0;i<s.size();i++){
if(!str_map.count(t[i]))
str_map[s[i]]=t[i];
if(str_map[s[i]]!=t[i])
return false;
}
return true;
}
};
// 上面整體的解題思路不對
// 利用兩個數組來實現 map映射的關系
class Solution {
public:
bool isIsomorphic(string s, string t) {
int num_ss[256]={0}, num_st[256]={0},n=s.size();
for(int i=0;i<n;i++){
if(num_ss[s[i]]!=num_st[t[i]])
return false;
num_ss[s[i]]=i+1; //要改變一起改變,要不就不改變
num_st[t[i]]=i+1;
}
return true;
}
};
451. Sort Characters By Frequency
class Solution {
public:
string frequencySort(string s) {
unordered_map<char,int> m;
string result;
map<int,vector<char>> m_freq;
for(auto c:s){
m[c]++;
}
for(auto x:m){
m_freq[x.second].push_back(x.first);
}
for(auto it=m_freq.rbegin();it!=m_freq.rend();it++){ // rbegin() 進行逆序輸出
// map有的操作,但是unordered_map沒有
for(auto c:it->second)// 遍歷vector中所有的字符串
result.append(it->first,c);
}
return result;
}
};
// 這個解法 有點東西
class Solution {
public:
string frequencySort(string s) {
unordered_map<char,int> freq;
vector<string> bucket(s.size()+1, "");
string res;
//count frequency of each character
for(char c:s) freq[c]++;
//put character into frequency bucket
for(auto& it:freq) {
int n = it.second;
char c = it.first;
bucket[n].append(n, c); // 這個語句沒看懂
}
//form descending sorted string
for(int i=s.size(); i>0; i--) {
if(!bucket[i].empty())
res.append(bucket[i]);
}
return res;
}
};
500. Keyboard Row
// 鍵盤元素
class Solution {
public:
vector<string> findWords(vector<string>& words) {
unordered_set<char> row1 {'q', 'w', 'e', 'r', 't', 'y','u', 'i', 'o', 'p'};
unordered_set<char> row2 {'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l'};
unordered_set<char> row3 { 'z', 'x', 'c', 'v', 'b' ,'n', 'm'};
vector<unordered_set<char>> rows {row1, row2, row3};
vector<string> result;
for(int i=0;i<words.size();i++){
int row=0;
for(int k=0;k<3;k++){
if(rows[k].count((char)tolower(words[i][0]))>0)
row=k;
}
result.push_back(words[i]);
for(int j=0;j<words[i].size();j++){
if(rows[row].count((char)tolower(words[i][j]))==0){
result.pop_back();
break;
}
}
}
return result;
}
};
508. Most Frequent Subtree Sum
// 自己想的思路完全正確
// 就是代碼沒能自己實現
//1.自下而上的計算所有結點的 node_subtree_sum1.自下而上的計算所有結點的 node_subtree_sum,並存儲在一個vector中
//2.對vector中的和及其出現的次數建立一個map
//3.輸出出現次數最多的 sum
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<int> findFrequentTreeSum(TreeNode* root) {
vector<int>result;
unordered_map<int,int> counts;
int max_count=INT_MIN;
subTreeSum(root,counts,max_count);
for(auto &x:counts){
if(x.second==max_count){
result.push_back(x.first);
}
}
return result;
}
// 自下而上而上的代碼要多看看,學習
int subTreeSum(TreeNode *root,unordered_map<int,int>&counts,int &max_count){
if(!root)
return 0;
int sum=root->val;
sum+=subTreeSum(root->left,counts,max_count);
sum+=subTreeSum(root->right,counts,max_count);
counts[sum]++;
max_count=max(max_count,counts[sum]);
return sum;
}
};
389. Find the Difference
// 很簡單的一道題
// 解題思路和205 一樣
// 都是建立一個 數組的hash來實現計數功能
class Solution {
public:
char findTheDifference(string s, string t) {
char temp;
int cnt[128]={0};
for(auto &c:s)
cnt[c]++;
for(auto &c:t){
cnt[c]--;
if(cnt[c]<0)
return c;
}
return temp;
}
};
409. Longest Palindrome
// 自己寫的版本
class Solution {
public:
int longestPalindrome(string s) {
int max_length=0;
map<char,int> m;
for(auto &c:s){
m[c]++;
}
for(const auto w:m){
if(w.second!=1)
max_length+=w.second;
}
return max_length+1;
}
};
//能用 vector就用 vector
// 一到字符串這塊就歇逼,哎
class Solution {
public:
int longestPalindrome(string s) {
vector<int> m(256, 0);
for (auto& c : s) m[c-'\0']++;
int result = 0;
for (auto& i : m) result += i%2 ? (result%2 ? i-1 : i) : i;
return result;
}
};
438. Find All Anagrams in a String
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
vector<int> pv(26,0), sv(26,0), res;
if(s.size() < p.size())
return res;
// fill pv, vector of counters for pattern string and sv, vector of counters for the sliding window
for(int i = 0; i < p.size(); ++i)
{
++pv[p[i]-'a'];
++sv[s[i]-'a'];
}
if(pv == sv)
res.push_back(0);
//here window is moving from left to right across the string.
//window size is p.size(), so s.size()-p.size() moves are made
for(int i = p.size(); i < s.size(); ++i)
{
// window extends one step to the right. counter for s[i] is incremented
++sv[s[i]-'a'];
// since we added one element to the right,
// one element to the left should be forgotten.
//counter for s[i-p.size()] is decremented
--sv[s[i-p.size()]-'a'];
// if after move to the right the anagram can be composed,
// add new position of window's left point to the result
if(pv == sv)
res.push_back(i-p.size()+1);
}
return res;
}
};