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