算法学习Day05 | 哈希表理论基础、LeetCode 242. 有效的字母异位词、349. 两个数组的交集、202. 快乐数、1. 两数之和

一、哈希表理论基础

二、LeetCode 242. 有效的字母异位词

题目链接:242. 有效的字母异位词

文章讲解:代码随想录 (programmercarl.com)

视频讲解:

1、排序

代码

class Solution {
public:
    bool isAnagram(string s, string t) {
        // 检查两个字符串的长度是否相等,如果不相等,直接返回false,因为长度不同的字符串无法成为字母异位词
        if (s.size() != t.size()) {
            return false;
        }

        // 对字符串s和t进行排序,使得其中的字符按照字母顺序排列 
        sort(s.begin(), s.end());
        sort(t.begin(), t.end());

        // 比较排序后的两个字符串是否相等,如果相等,说明它们包含的字符种类和数量完全一样,只是字符的顺序不同,因此是字母异位词
        return s == t;
    }
};

 复杂度分析

时间复杂度:O(nlog⁡n),其中 n 为 s 的长度。排序的时间复杂度为 O(nlog⁡n),比较两个字符串是否相等时间复杂度为 O(n),因此总体时间复杂度为 O(nlog⁡n+n)=O(nlog⁡n)。

空间复杂度:O(log⁡n)。排序需要 O(log⁡n) 的空间复杂度。

2、哈希表

代码

class Solution {
public:
    bool isAnagram(string s, string t) {
        // 检查两个字符串的长度是否相等,如果不相等,直接返回false,因为长度不同的字符串无法成为字母异位词
        if (s.size() != t.size()) {
            return false;
        }

        // 用于记录每个字母出现的次数
        int record[26] = {0};

        // 遍历字符串,每出现一次,对应位置的值加1
        for (int i = 0; i < s.size(); ++i) {
            record[s[i] - 'a']++;
        }

        // 遍历字符串,每出现一次,对应位置的值减1
        // 如果在减1的过程中,对应位置的值小于0,说明字符串s和t不是字母异位词,直接返回false
        for (int i = 0; i < t.size(); ++i) {
            record[t[i] - 'a']--;

            if (record[t[i] - 'a'] < 0) {
                return false;
            }
        }

        // 如果以上所有字符都成功完成了计数和减1操作,没有产生负值,说明字符串s和t是字母异位词,返回true  
        return true;
    }
};

复杂度分析

时间复杂度:O(n),其中 n 为 s 的长度。

空间复杂度:O(S),其中 S 为字符集大小,此处 S=26。

 三、LeetCode 349. 两个数组的交集

题目链接:349. 两个数组的交集

文章讲解:代码随想录 (programmercarl.com)

视频讲解:

1、使用set

代码

class Solution {  
public:  
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {  
        // 声明一个无序集合result_set,用于存储两个向量的交集元素  
        unordered_set<int> result_set;  
        // 将nums1向量转换为一个无序集合nums_set,这样可以更快地进行查找操作。其中使用begin()和end()函数来获取向量的起始和结束迭代器,以便将它们复制到nums_set中  
        unordered_set<int> nums_set(nums1.begin(), nums1.end());  
  
        // 遍历nums2中的每个元素,设其值为num  
        for (int num : nums2) {  
            // 检查num是否在nums_set中存在,如果存在则返回一个迭代器,否则返回end(),即指向nums_set的“末尾”(实际上是无效位置)。find()函数在哈希表中查找给定的值,如果不存在则返回end()  
            if (nums_set.find(num) != nums_set.end()) {  
                // 如果num存在于nums_set中,则将其添加到result_set中  
                result_set.insert(num);  
            }  
        }  
  
        // 返回result_set中的所有元素,作为一个新的整数向量。使用vector的构造函数,它接受两个迭代器(即result_set的开始和结束迭代器),并将它们复制到新的vector中  
        return vector<int>(result_set.begin(), result_set.end());  
    }  
};

复杂度分析 

  • 时间复杂度: O(m + n)
  • 空间复杂度: O(n)

2、使用数组

代码

class Solution {  
public:  
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {  
        // 声明一个无序集合result_set,用于存储两个向量的交集元素  
        unordered_set<int> result_set;  
        // 声明一个名为hash的数组,用于存储nums1中的元素及其对应的标记(存在或不存在)  
        int hash[1000] = {0};  
  
        // 遍历nums1中的每个元素,将元素的值作为索引,将该索引对应的标记设为1,表示该元素存在于nums1中  
        for (int num : nums1) {  
            hash[num] = 1;  
        }  
  
        // 遍历nums2中的每个元素,检查该元素是否存在于hash中,如果存在(即hash中对应索引的标记为1),则将该元素插入到result_set中  
        for (int num : nums2) {  
            if(hash[num] == 1) {  
                result_set.insert(num);  
            }  
        }  
          
        // 返回result_set中的所有元素,作为一个新的整数向量  
        return vector<int>(result_set.begin(), result_set.end());  
    }  
};

复杂度分析 

  • 时间复杂度: O(m + n)
  • 空间复杂度: O(n)

四、LeetCode 202. 快乐数

题目链接:202. 快乐数

文章讲解:代码随想录 (programmercarl.com)

视频讲解:

1、哈希法

代码

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) {  
        // 创建一个无序集合set,用于存储平方和,以便检查是否有重复  
        unordered_set<int> set;   
        int sum = 0;  
          
        // 无限循环,直到sum等于1或set中已有sum时停止  
        while (1) {  
            // 计算n的平方和 
            sum = getSum(n);  
  
            // 如果平方和sum等于1,说明是快乐数,返回true  
            if (sum == 1) {  
                return true;  
            }  
  
            // 如果set中已有sum,说明不是快乐数,返回false  
            if (set.find(sum) != set.end()) {  
                return false;  
            } else {  
                // 将新的平方和sum加入set中,并对n进行更新,为下一次循环做准备  
                set.insert(sum);  
                n = sum;  
            }  
        }  
    }  
};

 复杂度分析

  • 时间复杂度: O(logn)
  • 空间复杂度: O(logn)

2、双指针法

代码

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) {    
        // 计算n的平方和,赋值给slow,slow可以看作是n的移动轨迹或者说是“速度慢的点”  
        int slow = getSum(n);  
        // 计算slow的平方和,赋值给fast,fast可以看作是“速度快的点”或者说是指向slow的“指针”  
        int fast = getSum(getSum(n));  
  
        // 当fast不等于1且slow不等于fast时,执行下面的循环  
        while (fast != 1 && slow != fast) {  
            // slow的值加1,即求slow的平方和,相当于让slow这个“点”移动到新的位置  
            slow = getSum(slow);  
            // fast的值加1,即求fast的平方和,相当于让fast这个“指针”指向新的位置  
            fast = getSum(getSum(fast));  
        }  
        // 返回fast是否等于1的结果,即判断是否循环到最后得到1,如果是则返回true(真),否则返回false(假)  
        return fast == 1;  
    }    
};

复杂度分析

  • 时间复杂度:O(log⁡n)
  • 空间复杂度:O(1)

五、LeetCode 1.两数之和

题目链接:1. 两数之和

文章讲解:代码随想录 (programmercarl.com)

视频讲解:

1、暴力法

思路

枚举数组中所有的不同的两个下标的组合,逐个检查它们所对应的数的和是否等于target

代码

class Solution {  
public:  
    vector<int> twoSum(vector<int>& nums, int target) {  
        int n = nums.size();  
  
        // 遍历数组nums,第一个循环变量i从0开始到n-2  
        for (int i = 0; i < n - 1; ++i) {  
            // 第二个循环变量j从i+1开始到n-1  
            for (int j = i + 1; j < n; ++j) {  
                // 检查nums[i]和nums[j]的和是否等于目标值target  
                if (nums[i] + nums[j] == target) {  
                    // 如果和等于目标值,则返回一个包含i和j的向量  
                    return {i, j};  
                }  
            }  
        }  
        // 如果在数组中找不到两个数的和等于目标值,则返回一个空向量  
        return {};  
    }  
};

复杂度分析

时间复杂度:O(N^2),其中 N 是数组中的元素数量。最坏情况下数组中任意两个数都要被匹配一次。

空间复杂度:O(1)。

2、哈希表

思路

暴力法的时间复杂度较高的原因是寻找 target - x 的时间复杂度过高。因此,我们需要一种更优秀的方法,能够快速寻找数组中是否存在目标元素。如果存在,我们需要找出它的索引。

使用哈希表,可以将寻找 target - x 的时间复杂度降低到从 O(N) 降低到 O(1)。

创建一个哈希表,对于每一个 x,我们首先查询哈希表中是否存在 target - x,然后将 x 插入到哈希表中,即可保证不会让 x 和自己匹配。

代码

class Solution {  
public:   
    vector<int> twoSum(vector<int>& nums, int target) {    
        unordered_map<int, int> hashtable;  
          
        for (int i = 0; i < nums.size(); ++i) {  
            // 尝试在哈希表中查找键为target - nums[i]的元素,返回迭代器iter  
            auto iter = hashtable.find(target - nums[i]);  
  
            // 如果迭代器iter不等于hashtable的末尾(意味着找到了键为target - nums[i]的元素)  
            if (iter != hashtable.end()) {  
                // 返回一个包含iter->second和i的向量,即找到了两个数,它们的和等于目标值target  
                return {iter->second, i};  
            }  
  
            // 如果在哈希表中没有找到键为target - nums[i]的元素,则将nums[i]及其索引i添加到哈希表中  
            hashtable[nums[i]] = i;  
        }  
        // 如果遍历完整个数组nums都找不到两个数的和等于目标值target,则返回一个空向量  
        return {};  
    }  
};

复杂度分析

时间复杂度:O(N)。

空间复杂度:O(N)。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值