LeetCode刷题day6——哈希表part1

哈希表

哈希表理论基础
哈希表理论基础代码随想录链接
242.有效的字母异位词
题目链接/文章讲解/视频讲解
349. 两个数组的交集
题目链接/文章讲解/视频讲解
202. 快乐数
题目链接/文章讲解/视频讲解
1.两数之和
题目链接/文章讲解/视频讲解

学习时间

2024.6.11晚上-2024.6.12凌晨1点

自己看到题目的第一想法 VS 看完代码随想录之后的想法

  1. LeetCode——242.有效的字母异位词(数组)
    (1)自己:不记得哈希表怎么使用,暴力解法(后期有空尝试)
    (2)官方解题思路(赶紧这个分析思路很巧妙,但我太菜鸡想不到)
class Solution {
public:
    bool isAnagram(string s, string t) {
        if (s.length() != t.length()) {
            return false;
        }
        sort(s.begin(), s.end());
        sort(t.begin(), t.end());
        return s == t;
    }
};

(3)数组映射
看完代码随想录思路:字符映射到数组也就是哈希表的索引下标上,因为字符a到字符z的ASCII是26个连续的数值,所以字符a映射为下标0,相应的字符z映射为下标25。第一次遍历字符串对应的下标++,第二次遍历对应的下标–,然后看最终结果是否为0。注意本题是因为限制了数值的大小所以可以用数组映射

class Solution {
public:
    bool isAnagram(string s, string t) {
        int count[26] = {0};
        if (s.size() != t.size()) { // 一开始就判断长短是否相同
            return false;
        }
        for (int i = 0; i < s.size(); i++) {
            count[s[i] - 'a']++; // 或者-97(a的ASCII码),耗时更短
        }
        for (int j = 0; j < t.size(); j++) {
            count[t[j] - 'a']--;
        }
        for (int k = 0; k < 26; k++) {
            if (count[k] != 0) {
                return false;
            }
        }
        return true;
    }
};
  1. LeetCode——349.两个数组的交集 (set更好/数组)
    小点:直接使用set 不仅占用空间比数组大,而且速度要比数组慢,set把数值映射到key上都要做hash计算的。这个耗时,在数据量大的情况,差距是很明显的。
    unordered_set——可以无序,但是数值不可以重复
    (1)自己:
    a.思路:首先与上一题类似映射,count1001初始化为最大值(1001,因为题目有限制),对应数字的对应索引元素为对应数字;其次在nums2里循环,对应数字的对应索引元素为0;第三个for循环(count长度),寻找0,如果有的0,对res_set插入0元素对应的索引。这种方法应该是自动去重的了,但是考虑到3个for循环一直写。。感觉效率很低,所以后面闲下来可以补一下程序代码。

    b.思路:首先与上一题类似映射,对应数字的对应索引元素为对应数字;(好吧有点绕,图示理解);其次在nums2里循环与count对比,如果有的话,对res_set插入元素;但有个重要问题就是要对重复的进行移除操作
    在这里插入图片描述

错误示例:(没有去重)

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        vector<int> res_set;
        int count[1005] = {1005};
        for(int i=0; i<nums1.size(); i++){
            count[nums1[i]] = nums1[i];
        }
        for(int j=0; j<nums2.size(); j++){
            if(count[nums2[j]] == nums2[j])
            res_set.push_back(nums2[j]);//不是Push
        }
        return res_set;
    }
};

去重搜索了一下,set是最简单的,还有一种方法:
使用 std::unique 函数对数组进行原地去重的示例。std::unique 函数是 C++ 标准库中的一个算法,它会将相邻的重复元素移到容器的尾部,并返回一个指向新的逻辑末尾的迭代器。为了使其工作,需要先对数组进行排序,这样相同的元素会变得相邻。以下是具体的实现步骤:
i)对数组进行排序,使得相同的元素相邻。
ii)使用 std::unique 函数将相邻的重复元素移到容器的尾部,并获取新的末尾迭代器。
iii)使用返回的迭代器调整容器的大小,以删除多余的元素。
下面是完整的代码示例:

#include <iostream>
#include <algorithm> // 包含 std::sort 和 std::unique
#include <vector>

int main() {
    std::vector<int> arr = {1, 2, 2, 3, 4, 4, 5}; // 原始数组

    // 1. 对数组进行排序
    std::sort(arr.begin(), arr.end());

    // 2. 使用 std::unique 将相邻的重复元素移到尾部
    auto new_end = std::unique(arr.begin(), arr.end());

    // 3. 调整容器大小以去除多余的元素
    arr.erase(new_end, arr.end());

    // 打印去重后的数组
    for (int i : arr) {
        std::cout << i << " ";
    }
    std::cout << std::endl;

    return 0;
}

运行上述代码,输出结果将是:

1 2 3 4 5

解释:
std::sort(arr.begin(), arr.end()); 对数组进行排序,使得相同的元素相邻。
std::unique(arr.begin(), arr.end()); 将相邻的重复元素移到容器的末尾,并返回一个指向新逻辑末尾的迭代器。
arr.erase(new_end, arr.end()); 删除从新逻辑末尾到实际末尾之间的所有元素,完成去重操作。
这样,我们就通过 std::unique 函数实现了数组的原地去重。

(2)看了代码随想录
自己是真菜,想的思路极其繁琐

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int>
            result_set; // 存放结果,之所以用set是为了给结果集去重
        unordered_set<int> nums_set(nums1.begin(),nums1.end()); // 将数组转化为set去重
        
        //使用引用 (int& 或 const int&) 可以直接修改或防止拷贝元素,提升性能
        for (const int& num : nums_set) {
        cout << num << " "; // 打印修改后的元素
        }
        
        for (int num : nums2) { 
        	//int 也可以替换为auto关键字可以让代码更通用,不必显式指定元素类型。
            // 发现nums2的元素 在nums_set里又出现过
            if (nums_set.find(num) != nums_set.end()) {
                result_set.insert(num);
            }
        }
        return vector<int>(result_set.begin(), result_set.end());
    }
};
  1. LeetCode——202.快乐数(set)
    (1)自己:把一个数拆成每个单位平方(不会);然后sum!=1循环,怎么用映射呢?
    (2)看完代码随想录:题目中说了会 无限循环,那么也就是说求和的过程中,sum会重复出现解题很重要! 当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法了。
class Solution {
public:
    int GetNewSum(int n) {
        int sum = 0;
        while (n) {
            sum += (n % 10) * (n % 10);
            n = n / 10;
        }
        return sum;
    }
    bool isHappy(int n) {
        unordered_set<int> res;
        while (1) {
            int sum = GetNewSum(n);
            if (sum == 1) { 
                return true;
            }

            if (res.find(sum) != res.end()) {//找到了返回对应的key,没找到=end()
                return false;
            } else {
                res.insert(sum);
            }
            n = sum;
        }
    }
};
  1. LeetCode——1.两数之和(map)
    map基本操作
    (1)自己思路:从索引0开始,到size-1,因为循环到最后就一个数了,肯定不满足条件。
    一个循环每个值;另一遍循环找后面元素是否=target - 当前的值。AC
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        for(int i=0; i<nums.size()-1; i++)
        {
            for(int j = i; j<nums.size()-1; j++){
                if(nums[j+1] == (target - nums[i])){
                    return vector<int>{i,j+1};
                }
            }
        }
        return vector<int>{0,0};
    }
};

(2)看完视频讲解:map{key,value}
这道题 我们需要 给出一个元素,判断这个元素是否出现过,如果出现过,返回这个元素的下标。
那么判断元素是否出现,这个元素就要作为key,所以数组中的元素作为key,有key对应的就是value,value用来存下标。

所以 map中的存储结构为 {key:数据元素,value:数组元素对应的下标}。

// class Solution {
// public:
//     vector<int> twoSum(vector<int>& nums, int target) {
//         for(int i=0; i<nums.size()-1; i++)
//         {
//             for(int j = i; j<nums.size()-1; j++){
//                 if(nums[j+1] == (target - nums[i])){
//                     return vector<int>{i,j+1};
//                 }
//             }
//         }
//         return vector<int>{0,0};
//     }
// };

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        // key存储数组值,value存储数组索引;
        // 注意题目是查找元素是否出现,而map可以快速查找key,所以用key存储元素
        // 题目返回数组下标,所以用value存储索引
        unordered_map<int, int> map;
        for (int i = 0; i < nums.size(); i++) {
            int find_s = target - nums[i]; // 要查询的值
            if (map.find(find_s) != map.end()) {
                return { map[find_s], i};
            } else {
                map.insert(nums[i],i); //key,value都要存 !!!error
            }
        }
        return {};
    }
};

编译错误,对Map的insert错误使用,修改方法:
a. for中修改——卡哥代码:auto可以让iter作为一个指针

auto iter = map.find(target - nums[i]); 
            if(iter != map.end()) {
                return {iter->second, i};
            }
map.insert(pair<int, int>(nums[i], i)); 

b. else中修改

map[nums[i]] = i; // key,value都要存
或者
map.insert(pair<int, int>(nums[i], i)); 
或者
map.emplace(nums[i],i);

自己实现的困难

1、定义数组
(1)int record[26] = {0};
这是一个标准的C++数组声明,表示一个包含26个整数的数组,并且所有元素都初始化为0。

int record[26] = {0};

特点
类型:数组中的每个元素都是一个 int 类型的整数。
大小:固定为26个元素,不能动态调整大小。
初始化:所有元素被初始化为0。若未显式初始化,默认情况下,未指定初始化值的元素将全部被初始化为0。
内存分配:在栈上分配内存。
(2) vector record[26] = {0};
这是一个包含26个 std::vector 的数组,并且试图将第一个向量初始化为0。这种写法不符合预期的用法,因为 std::vector 不接受单个整数来初始化,而是通常通过列表初始化或默认构造函数进行初始化。

#include <vector>
using namespace std;
vector<int> record[26];

特点:
类型:数组中的每个元素都是一个 std::vector 对象。
大小:固定为26个元素,但每个 std::vector 本身可以动态调整大小。
初始化:未显式初始化的情况下,每个 std::vector 默认构造为空的向量。你可以在后续代码中添加元素。
内存分配:数组本身在栈上分配,但 std::vector 中的元素在堆上分配。

2、思路繁琐

3、怎么将一个数拆成每个单位的平方,以及怎么映射?
没注意无限循环的隐藏条件!

4、对map一点都不记得。。

今日收获

1、数组映射;2、set映射;3、map映射
知识点:for()循环的另外一种写法;auto的使用

  • 20
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值