LeetCode Day6 哈希表: 242.有效的字母异位词,349. 两个数组的交集,202. 快乐数,1. 两数之和

目录

哈希表理论基础

242.有效的字母异位词

(1) 题目描述 

(2) 解题思路

349. 两个数组的交集

(1) 题目描述 

(2) 解题思路

202. 快乐数

(1) 题目描述 

(2) 解题思路

1. 两数之和 

(1) 题目描述 

(2) 解题思路


哈希表理论基础

242.有效的字母异位词

(1) 题目描述 

LeetCode 242. 有效的字母异位词

代码随想录:(文字版)

代码随想录:(视频版)

(2) 解题思路

解题步骤:

1. 暴力解法

  • 第一层for循环: 遍历s中的元素
  • 第二层for循环: 遍历t中的元素,判断s中元素是否在t中出现。注意判断字母是否重复。

 2. 哈希表

哈希表: 快速判断一个元素是否出现集合里。

本题目只有26个字母,数值小,范围可控 --> 数据结构使用数组

数组大小: 字符a到字符z的ASCII是26个连续的数值 -->数组大小为26,初始化为0。

class Solution {
public:
    bool isAnagram(string s, string t) {
        // 哈希表法
        // (1) 定义大小为26的数组作为哈希表,因为字母为26个,初始值均为0,字母a位于哈希表数组下标为0的位置。
        int record[26] = {0};
        // (2) 统计在s中字母的出现次数,出现一次加1
        for (int i = 0; i < s.size(); i++){
            // s[i]: s中第i个字符的ASCII码
            //'a': 字符a的ASCII码(计算机内部的二进制表示)
            record[s[i]-'a']++; // s[i]-'a': s中第i个字符在字母表的位置,即record中对应字符的下标位置
        }
        // (3) 统计在t中字母的出现次数,出现一次在哈希表中减1
        for (int i = 0; i < t.size(); i++){
            record[t[i]-'a']--;
        }
        // (4) 根据哈希表中的数值判断结果
        // (4.1) record数组如果有的元素不为零0,说明字符串s和t一定是谁多了字符或者谁少了字符。
        for (int i = 0; i < 26;i++){
            if(record[i] != 0){
                return false;
            }
        }
        // (4.2) record数组所有元素都为零0,说明字符串s和t是字母异位词
        return true;
    }
};

349. 两个数组的交集

(1) 题目描述 

LeetCode 349. 两个数组的交集

代码随想录:(文字版)

代码随想录:(视频版)

(2) 解题思路

题目说明:输出结果中的每个元素一定是唯一的,即去重(无序)

1. 数据结构set

使用unordered_set: 读写效率是最高的,不重复,无序,且不能更改数值。

C++去重容器 unordered_set 用法概述

cppreference: std::unordered_set

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        // 数据结构set
        // (1) 定义unordered_set存放结果,用set是给结果集去重
        unordered_set<int> result_set;
        // (2) 用unordered_set构造哈希表,将nums1中数据映射到哈希表
        unordered_set<int> nums_set(nums1.begin(), nums1.end());
        // (3) 用for循环遍历nums2中元素,检测每个元素是否是交集元素
        for (int i = 0; i < nums2.size(); i++){
            int num = nums2[i];     // * 上面和这行等效于 for (int num: nums2)
            // (4) 检查nums2中的元素num是否在nums1映射的哈希表nums_set中出现过,如果出现,则是交集元素,将其存入结果集
                // 注意: set.end() 返回的是最后的index的下一个, 即是不存在的;
                // set.find(x) = set.end(): 没找到;
            if (nums_set.find(num) != nums_set.end()){
                result_set.insert(num);
            }
        }
        // (5) 转换为题目要求的数据结构
        return vector<int>(result_set.begin(), result_set.end());
    }
};

2. 数据结构数组

使用条件:  本题后面 数值范围有限:

  • 1 <= nums1.length, nums2.length <= 1000
  • 0 <= nums1[i], nums2[i] <= 1000

优点: 运算速度

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        // 数据结构数组
        // (1) 定义unordered_set存放结果,用set是给结果集去重
        unordered_set<int> result_set;
        // (2) 用数组构造哈希表,将nums1中数据映射到哈希表,数组大小比题目条件中的1000稍大即可,初始化均为0
        int hash[1001] = {0};
        // (3) 用for循环遍历nums1中元素,出现的字母在hash数组中做记录
        for(int num:nums1){
            hash[num] = 1;
        }
        // (4) 用for循环遍历nums2中元素,当nums2中元素在nums1中出现时,将其加入结果集
        for (int i = 0; i < nums2.size(); i++){
            int num = nums2[i];     // * 上面和这行等效于 for (int num: nums2)
            if (hash[num] == 1){
                result_set.insert(num);
            }
        }
        // (5) 转换为题目要求的数据结构
        return vector<int>(result_set.begin(), result_set.end());
    }
};

202. 快乐数

(1) 题目描述 

LeetCode 202. 快乐数

代码随想录:(文字版)

(2) 解题思路

基本解法思路:

  • 求该数字的每一位的平方和,得到新数字
  • 将新数字更新,重复上一步,直到判断结果是否为1

 但难点在于该数字会陷入无限循环 --> 使用哈希表判断该数字是否出现过,出现则表示已经开始无限循环,返回false。 

class Solution {
public:
    // 取数值各个位上的单数之和
    int getSum(int n) {
        int sum = 0;
        while (n) { // 一直循环,直到取完n的每个位置的平方和
            sum += (n % 10) * (n % 10); // n % 10: n除以10以后的余数(取余) --> 个位的数
            n /= 10; // n /= 10: 相当于n=n/10,是得到n除以10的商的整数部分(取整)--> 将n去掉个位剩下的数。如19 --> 1
        }
        return sum;
    }
    bool isHappy(int n) {
        // 哈希表
        // (1) 定义unordered_set来判断sum是否重复出现,避免陷入无限循环
        unordered_set<int> set;
        // (2) 循环求数字n的每一位的平方和sum,得到后更新数字n,直到找到结果
        while(1) {
            int sum = getSum(n);
            // (2.1) 找到最终结果为1的快乐数
            if (sum == 1) {
                return true;
            }
            // (2.2) 使用哈希表检查sum是否重复出现,当开始无限循环时
            if (set.find(sum) != set.end()) { // 如果这个sum曾经出现过,说明已经陷入了无限循环了,立刻return false
                return false;
            } else {
                set.insert(sum); // 如果这个sum没有出现过,将其加入哈希表来帮助接下来的判断
            }
            n = sum; // 更新数字n,直到找到结果
        }
    }
};

1. 两数之和 

(1) 题目描述 

LeetCode 1. 两数之和

代码随想录:(文字版)

代码随想录:(视频版)

(2) 解题思路

1. 暴力解法

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        // 暴力解法
        // (1) 定义根据题目,结果为两个下标
        vector<int> result(2, 0);
        // (2) 两层for循环,找到两个元素和为目标值
        for (int i = 0; i < nums.size(); i++){
            int sum = 0;
            for (int j = i + 1; j < nums.size(); j++){ // 注意,j的初始值为i+1,因为元素不能重复
                sum = nums[i] + nums[j];
                if (sum == target){
                    result[0] = i;
                    result[1] = j;
                    break; // 题目说明,只存在一个答案,故一旦找到即可break
                }
            }
        }
        return result;
    }
};

2. 哈希表map

  •   为什么会想到用哈希表
    •  本题需要一个集合来存放遍历过的元素,然后在遍历数组的时候去询问这个集合,某元素是否出现在这个集合 --> 哈希表
  • 哈希表为何用map

数组和set和map来做哈希法:

  • 数组的大小是受限制的,如果元素很少,而哈希值太大-->内存空间浪费。
  • set是一个集合,里面放的元素只能是一个key,不能记录下标位置。
  • map ,map是一种key value的存储结构,可以用key保存数值,用value再保存数值所在的下标。
  • map类型
    • 本题不需要key有序,选择std::unordered_map 效率更高
  • map中的key和value用来存什么的
    • key:  判断key是否出现     value: key对应信息   
    •   map中的存储结构为 {key:数据元素,value:数组元素对应的下标。
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        // 哈希表map
        // (1) 定义map哈希表,kay存放元素,value存放下标
        std::unordered_map<int, int> map;
        // (2) 遍历nums中元素,并寻找与其和为target的另一个元素,使用哈希表查询另一个元素是否已经遍历到
        for(int i = 0; i < nums.size(); i++){
            // 寻找与其和为target的另一个元素
            auto iter = map.find(target - nums[i]); // //find返回的是迭代器,即指针
            // 使用哈希表查询另一个元素是否已经遍历到,
            if (iter != map.end()){ 
                // 当发现已经出现过,返回结果值
                return {iter->second, i};  //用迭代器访问pair的first或者second成员方法是:iter->first或iter->second
            }
            // 当发现没有出现过,将当前元素加入map
            map.insert(pair<int, int>(nums[i], i));
        }
        return {};
    }
};

  • 25
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
给定一个整数组 nums 和一个目标值 target,要求在数组中找出两个的和等于目标值,并返回这两个的索引。 思路1:暴力法 最简单的思路是使用两层循环遍历数组的所有组合,判断两个的和是否等于目标值。如果等于目标值,则返回这两个的索引。 此方法的时间复杂度为O(n^2),空间复杂度为O(1)。 思路2:哈希表 为了优化时间复杂度,可以使用哈希表来存储数组中的元素和对应的索引。遍历数组,对于每个元素nums[i],我们可以通过计算target - nums[i]的值,查找哈希表中是否存在这个差值。 如果存在,则说明找到了两个的和等于目标值,返回它们的索引。如果不存在,将当前元素nums[i]和它的索引存入哈希表中。 此方法的时间复杂度为O(n),空间复杂度为O(n)。 思路3:双指针 如果数组已经排序,可以使用双指针的方法来求解。假设数组从小到大排序,定义左指针left指向数组的第一个元素,右指针right指向数组的最后一个元素。 如果当前两个指针指向的的和等于目标值,则返回它们的索引。如果和小于目标值,则将左指针右移一位,使得和增大;如果和大于目标值,则将右指针左移一位,使得和减小。 继续移动指针,直到找到两个的和等于目标值或者左指针超过了右指针。 此方法的时间复杂度为O(nlogn),空间复杂度为O(1)。 以上三种方法都可以解决问题,选择合适的方法取决于具体的应用场景和要求。如果数组规模较小并且不需要考虑额外的空间使用,则暴力法是最简单的方法。如果数组较大或者需要优化时间复杂度,则哈希表或双指针方法更合适。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值