目录
哈希表理论基础、筆記
甚麼是雜湊表
雜湊函數(Hash Function)
Hash Value 大於 Table Size
兩組 Hash Value 相同
拉链法(chaining)
线性探测法
雜湊表常見的資料型態
數組
Set
Map
讀題
242.有效的字母异位词
看完代码随想录之后的想法
349. 两个数组的交集
看完代码随想录之后的想法
1. 两数之和
看完代码随想录之后的想法
202. 快乐数
第一眼看到題目的想法
看完代码随想录之后的想法
HashTable 數組實作 - 242.有效的字母异位词
思路
Code
第一種思路 → 空間換取時間
卡哥作法 → 時間換取空間
HashTable Set 實作 - 349. 两个数组的交集
思路
Set
數組
Code
Set
數組
HashTable Set 實作 - 202. 快乐数
思路
Code
HashTable Map 實作 - 1. 两数之和
思路
Code
總結
自己实现过程中遇到哪些困难
今日收获,记录一下自己的学习时长
相關資料
昨日算法營休息,今日繼續!
雜湊表可以想成就是一個數組會比較好理解
資料型態 | 插入/刪除 (時間複雜度) | 查詢(時間複雜度) | 適用場景 |
數組 | O(n)O(n)O(n) | O(1)O(1)O(1) | 數據固定 頻繁查詢 較少增刪 |
我們可以想像,如果今天我們知道我們的數據是放在哪個位置,使用數組就可以直接存取到那個位置的資料,我們只需要花 $O(1)$ 的時間就可以直接存取到我們要的資料。
這時會有一個問題,我要怎麼快速知道我要的資料是在哪個位置,這時候就會用到雜湊函數,我們用下面的圖來解釋
但這樣還是會有一個問題,假設我的數值超過了Hash Table的大小呢? 並且HastTable本身是一個數組,那也會非常吃記憶體,不可能無限擴大。
這樣還是會有一個問題,叫做碰撞,假設我有兩個資料的Hash Value經過一連串的處理後,雖然小於Table Size,但兩個資料的HashValue一樣呢?
如下圖,我們可以發現當Value = B 與 H時,Hash Value都會等於0。
這樣子我們有兩種方式進行處理
如果發生碰撞,一定代表碰撞的元素都對應在同一個內,那就可以使用****拉链法(chaining)****進行處理,處理方式就是一但遇到碰撞,就在建立一個LinkList節點,讓這個hashtable的位置指向這個LinkList的節點,就像一條鍊子一樣,只要在這一條上,都是同樣的hashvalue,如下圖
使用线性探测法 TableSize一定要大於DataSize ,因為线性探测法假設遇到碰撞,他就會在看這個hashvalue的下一個位置有沒有數值,如果沒有,則會去占用這個位置,如下圖所示
在雜湊表當中主要會有三個常用的資料結構,分別是: 數組、set、map
使用情境大致如下
在數據量比較小的狀態下,我可以使用數組來實現hashtable 而裡面的hashfunction我可以自己設計,就像是有效的字母异位词 可以用"-'a'" (類似hashfunction)來找到對應的hashvalue,並存入count值
在使用set的狀況下,跟數組狀況很像,但數據量太大,使用array會佔用太多內存空間 所以使用set,在unordered_set中,底層會對數值進行hash運算後,找出對應的下標存入我們的值(?這裡我搞不太 懂set的底層邏輯),因為數值不能重複,類似於數組中我們不存count,是固定存"1"
在使用map的狀況,主要是因為我們需要對兩個值進行索引 所以在使用在unordered_map中,使用狀況是,需要進行key-value的對應,可以設定key值是多少,map會對key值進行hash計算,得出hashvalue,並存入key的對應value到hashvalue的位置,
之前對於Hash表很陌生,所以在一開始的三題主要都是看卡哥的講解影片,在看的有效的字母異位詞時,的確卡哥一開始的暴力解非常的直覺,但hash的做法點通了,其實整體不難,就是要將key以及value的對應做好,剛好這個題目的key就是’a’ - ‘z’ 而value就是其中的count,用這點去理解就會快很多了
在看完卡哥的講解,以及在算法訓練營將自己對於set的想法統整之後,並得到卡哥的確認,對於這題的想法就比較清晰了,原本這題做set的會比較好,但因為Leetcode的條件以及測資改變,用數組所佔用的空間也不大,所以用數組相對來說會比較好,因為數值不大,但用set的話效能較差的原因是因為set內部還要再做hash運算等操作,整體會稍慢一點。
Leetcode的第一題,也是HashTable中Map的使用經典題目,之前有做過,聽卡哥的講解後更清晰了。
看到題目很值覺得想到,就是要將數值進行拆分並且作平方,這個數值可能會很大,但我們只關注這個有沒有配對是快樂數,所以不需要用到兩個值存儲,但詳細要怎麼猜分真的沒有想法
取數值的各個位數上的單數之和,卡哥點通知後,對於這道題就有比較清晰的想法了,原來是取10的餘數後逐步地增位數也就是除10,像下圖,這樣就可以逐步去取sum了
卡哥作法
++
;--
;class Solution {
public:
bool isAnagram(string s, string t) {
if(s.size() != t.size()){
return false;
}
int count1[26] = {0};
int count2[26] = {0};
for(int i = 0; i < s.size(); i++){
count1[s[i] - 'a']++;
count2[t[i] - 'a']++;
}
for(int i = 0; i < 26; i++){
if(count1[i] != count2[i]){
return false;
}
}
return true;
}
};
class Solution {
public:
bool isAnagram(string s, string t) {
if(s.size() != t.size()){
return false;
}
int count[26] = {0};
for(int i = 0; i < s.size(); i++){
count[s[i] - 'a']++;
}
for(int i = 0; i < t.length(); i++){
count[t[i] - 'a']--;
}
for(int i = 0; i < 26; i++){
if(count[i] != 0){
return false;
}
}
return true;
}
};
class Solution {
public:
vector intersection(vector& nums1, vector& nums2) {
unordered_set result_set;
unordered_set nums_set(nums1.begin(), nums1.end());
for(int num : nums2){
if (nums_set.find(num) != nums_set.end()){
result_set.insert(num);
}
}
return vector(result_set.begin(), result_set.end());
}
};
class Solution {
public:
vector intersection(vector& nums1, vector& nums2) {
unordered_set result_set;
int hash[1002] = {0};
for(int num : nums1){
hash[num] = 1;
}
for(int num : nums2){
if(hash[num] == 1){
result_set.insert(num);
}
}
return vector(result_set.begin(), result_set.end());
}
};
class Solution {
public:
int getsum(int n) {
int sum = 0;
while(n) {
sum += (n % 10) * (n % 10);
n /= 10;
}
return sum;
}
bool isHappy(int n) {
unordered_set set;
while(1){
int sum = getsum(n);
if(sum == 1) {
return true;
}
if(set.find(sum) != set.end()) {
return false;
} else {
set.insert(sum);
}
n = sum;
}
}
};
class Solution {
public:
vector twoSum(vector& nums, int target) {
unordered_map map;
for(int i =0; i < nums.size(); i++){
auto iter = map.find(target - nums[i]); // 使用auto 自動辨識資料型態
if(iter != map.end()) {
return {iter->second, i};
}
map.insert(pair(nums[i], i));
}
return {};
}
};
今天在過程中,對於HashTable有幾個部分不熟悉
今天很大部分都是看卡哥的講解影片,一步步跟著做,我現在只能對自己說,在這個過程中我發現很多我以為我知道的概念或者實際進行操作時,要如何使用C++實現都有遇到困難,但這也代表著我就是一步步地去學習紀錄,讓這次的一刷學習的更加的紮實。
哈希表理论基础
文章讲解:https://programmercarl.com/哈希表理论基础.html
题目链接/文章讲解/视频讲解: https://programmercarl.com/0242.有效的字母异位词.html
题目链接/文章讲解/视频讲解:https://programmercarl.com/0349.两个数组的交集.html
题目链接/文章讲解:https://programmercarl.com/0202.快乐数.html
题目链接/文章讲解/视频讲解:https://programmercarl.com/0001.两数之和.html