代碼隨想錄算法訓練營|第六天|哈希表理论基础、242.有效的字母异位词、349.两个数组的交集、202.快乐数、1.两数之和。刷题心得(c++)

目录

哈希表理论基础、筆記

甚麼是雜湊表

雜湊函數(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 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的使用經典題目,之前有做過,聽卡哥的講解後更清晰了。

  1. 因為題目要求回傳兩數之和的”位置”,位置這點很重要,又因為我們要找出符合Target的”數值”所以在這個HashTable當中,就會需要存放兩個東西,一個是數值、一個是位置
  2. 並且因為我們需要快速找到當下位置中數值與Target的差值有沒有在HashTable當中,所以我們需要將數值當作key,讓hashfunction可以快速查找
  3. 假設沒有找到,則將當下位子的數值以及位置放入hashtable當中。

202. 快乐数

第一眼看到題目的想法

看到題目很值覺得想到,就是要將數值進行拆分並且作平方,這個數值可能會很大,但我們只關注這個有沒有配對是快樂數,所以不需要用到兩個值存儲,但詳細要怎麼猜分真的沒有想法

看完代码随想录之后的想法

取數值的各個位數上的單數之和,卡哥點通知後,對於這道題就有比較清晰的想法了,原來是取10的餘數後逐步地增位數也就是除10,像下圖,這樣就可以逐步去取sum了

HashTable 數組實作 - 242.有效的字母异位词

思路

  1. 假設兩組數組長度不同,直接return false,因為這樣步可能出現有效的字母異位詞
  2. 將字串轉為array.
  3. 建立兩個count[26] → hashtable
  4. 建立for迴圈遍歷所有元素
    1. 終止條件為遍歷完字串長度
    2. 每個位置都減’a’讓下標直接對應0~25 → 可以想像成hashfunction.
    3. 對應位置count++;
  5. 建立for迴圈遍歷兩個count的計數,假設不同直接return false。遍歷完一致則return true。

卡哥作法

  1. 假設兩組數組長度不同,直接return false,因為這樣步可能出現有效的字母異位詞
  2. 將字串轉為array.
  3. 建立一個count[26] → hashtable
  4. 建立for迴圈遍歷第一個字串所有元素
    1. 終止條件為遍歷完字串長度
    2. 每個位置都減’a’讓下標直接對應0~25 → 可以想像成hashfunction.
    3. 對應位置count++;
  5. 建立for迴圈遍歷第二個字串所有元素
    1. 終止條件為遍歷完字串長度
    2. 每個位置都減’a’讓下標直接對應0~25 → 可以想像成hashfunction.
    3. 對應位置count--;
  6. 建立for迴圈遍歷count所有元素
    1. 終止條件為遍歷26次
    2. 假設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

  1. 建立一個set存放結果 → 去除重複數值
  2. 將num1轉化成set
  3. 遍歷所有num2的元素,假設有出現過則插入結果set
  4. return 結果set

數組

  1. 建立一個result_set存放結果 → 去除重複數值
  2. 建立hashtable = {0}
  3. 將num1 所有元素遍歷過一次,數值皆對應hashtable的索引,假設有數值,則標記為1
  4. 遍歷過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. 快乐数

思路

  1. 建立一個set 儲存所有sum
  2. 找出如何計算各個位數的平方和方法
  3. 假設sum == 1 則return true;
  4. 假設在set中有重複的sum,則return false;
  5. 如果沒有則將此次的sum插入到set中
  6. 最後將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. 两数之和

思路

  1. 建立一個map ,存放數組中的數值以及位置
  2. 建立遍歷nums中的所有元素
  3. 尋找當下位置中數值與Target的差值有沒有在map當中
  4. 如果有找到回傳對應值的位置以及當下位置
  5. 假設沒有找到,則將當下位子的數值以及位置放入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有幾個部分不熟悉

  1. set跟map的定義沒有很清楚
  2. c++中的數組、set、map的操作不熟
  3. 對於取各個位數的值出現困難

今日收获,记录一下自己的学习时长

今天很大部分都是看卡哥的講解影片,一步步跟著做,我現在只能對自己說,在這個過程中我發現很多我以為我知道的概念或者實際進行操作時,要如何使用C++實現都有遇到困難,但這也代表著我就是一步步地去學習紀錄,讓這次的一刷學習的更加的紮實。

相關資料

哈希表理论基础

文章讲解:https://programmercarl.com/哈希表理论基础.html

  1. 有效的字母异位词

题目链接/文章讲解/视频讲解: https://programmercarl.com/0242.有效的字母异位词.html

  1. 两个数组的交集

题目链接/文章讲解/视频讲解:https://programmercarl.com/0349.两个数组的交集.html

  1. 快乐数

题目链接/文章讲解:https://programmercarl.com/0202.快乐数.html

  1. 两数之和

题目链接/文章讲解/视频讲解:https://programmercarl.com/0001.两数之和.html

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值