代码随想录算法训练营第6天|哈希表理论基础、242.有效的字母异位词、349. 两个数组的交集、202. 快乐数、1. 两数之和

文章目录

  • 哈希表理论基础
    • 哈希碰撞的两种解决方式
      • 拉链法
      • 线性探测法
    • 常见的三种哈希结构
  • 242.有效的字母异位词
    • 思路
    • 代码
  • 349. 两个数组的交集
    • 思路
    • 代码
  • 202. 快乐数
    • 思路
    • 代码
      • 写代码犯的错误
  • 1. 两数之和
    • 思路
    • 代码
  • 今日收获

哈希表理论基础

学习链接:哈希表理论基础

哈希表用于快速判断一个元素是否出现在集合里
哈希函数:将其他类型数据格式转化为不同的数值(数据和索引的映射)
哈希碰撞:不同的数据映射到同一索引上

哈希碰撞的两种解决方式

拉链法

发生冲突的元素存储在链表中
代码随想录算法训练营第6天|哈希表理论基础、242.有效的字母异位词、349. 两个数组的交集、202. 快乐数、1. 两数之和_第1张图片
选择适当的哈希表大小,使得既不会因为数组空值而浪费大量内存,也不会因为链表太长而在查找上浪费太多时间

线性探测法

向下找一个空间存放冲突的元素,要求tableSize>dataSize
代码随想录算法训练营第6天|哈希表理论基础、242.有效的字母异位词、349. 两个数组的交集、202. 快乐数、1. 两数之和_第2张图片

常见的三种哈希结构

数组,set,map
代码随想录算法训练营第6天|哈希表理论基础、242.有效的字母异位词、349. 两个数组的交集、202. 快乐数、1. 两数之和_第3张图片
代码随想录算法训练营第6天|哈希表理论基础、242.有效的字母异位词、349. 两个数组的交集、202. 快乐数、1. 两数之和_第4张图片
当我们要使用集合来解决哈希问题的时候,优先使用unordered_set,因为它的查询和增删效率是最优的,如果需要集合是有序的,那么就用set,如果要求不仅有序还要有重复数据的话,那么就用multiset。
哈希法也是牺牲了空间换取了时间,因为我们要使用额外的数组,set或者是map来存放数据,才能实现快速的查找

242.有效的字母异位词

题目链接:242.有效的字母异位词
文章讲解:代码随想录|242.有效的字母异位词
视频讲解:学透哈希表,数组使用有技巧!Leetcode:242.有效的字母异位词

思路

判断字符串的每一个字符出现在字符的集合(26个字母)中的次数,用哈希表
这里的集合大小固定且数量较小,因此用简单结构——数组,相比于set运算更快
第一个字符串s在哈希表中record[s[i] - ‘a’]++
第二个字符串t在哈希表中record[s[i] - ‘a’]–
如果最终哈希表中仍然全是0,则每个字符出现的次数一样,判定两个字符串为字母异位词

代码

class Solution {
public:
    bool isAnagram(string s, string t) {
        int record[26] = {0};
        for (int i = 0; i < s.size(); i++) {
            // 并不需要记住字符a的ASCII,只要求出一个相对数值就可以了
            record[s[i] - 'a']++;
        }
        for (int i = 0; i < t.size(); i++) {
            record[t[i] - 'a']--;
        }
        for (int i = 0; i < 26; i++) {
            if (record[i] != 0) {
                // record数组如果有的元素不为零0,说明字符串s和t 一定是谁多了字符或者谁少了字符。
                return false;
            }
        }
        // record数组所有元素都为零0,说明字符串s和t是字母异位词
        return true;
    }
};

349. 两个数组的交集

题目链接:349. 两个数组的交集
文章讲解:代码随想录|349. 两个数组的交集
视频讲解:学透哈希表,set使用有技巧!Leetcode:349. 两个数组的交集

思路

如果这道题没有限制数值的大小,则无法使用数组,而且如果哈希值比较少,又跨度非常大,用数组会造成极大的空间浪费
此时需要用set,set在c++中提供了三种可用的数据结构:set, multiset, unordered_set
std::set和std::multiset底层实现都是红黑树,std::unordered_set的底层实现是哈希表, 使用unordered_set 读写效率是最高的(在树中搜索效率低),并不需要对数据进行排序,而且还不要让数据重复,所以选择unordered_set
题目要求结果没有重复的值且无序,因此也可以用unordered_set来存储result,记得返回值要改回成题目要求的数据格式
代码随想录算法训练营第6天|哈希表理论基础、242.有效的字母异位词、349. 两个数组的交集、202. 快乐数、1. 两数之和_第5张图片

代码

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());
        for (int num : nums2) {
            // 发现nums2的元素 在nums_set里又出现过
            if (nums_set.find(num) != nums_set.end()) {
                result_set.insert(num);
            }
        }
        return vector<int>(result_set.begin(), result_set.end());
    }
};

注:如果nums_set.find(num)没找到num则会返回nums_set.end();for(int num:num2)这个用法可以用于任何支持迭代器的容器

202. 快乐数

题目链接:202. 快乐数
文章讲解:代码随想录|202. 快乐数

思路

如果始终变不到1,则说明进入了无限循环,也就是这个平方和sum会重复出现,所以可以把sum保存在一个集合里,每次求出来一个sum就查找一下,如果找到了就说明有重复的,会进入无限循环
显而易见,可以用哈希表进行查询
此题的重点是get到无限循环中sum会重复出现

代码

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;
            }
            // 如果这个sum曾经出现过,说明已经陷入了无限循环了,立刻return false
            if (set.find(sum) != set.end()) {
                return false;
            } else {
                set.insert(sum);
            }
            n = sum;
        }
    }
};

写代码犯的错误

要先判断该sum是否出现,然后再把这个sum放到集合里,如果先放到集合里再判断,则一定会查询到,这样的话就一定会return false
犯了个低级错误,写代码前一定要思考好其中的逻辑再写!

1. 两数之和

题目链接:1. 两数之和
文章讲解:代码随想录|1. 两数之和
视频讲解:梦开始的地方,Leetcode:1.两数之和,学透哈希表,map使用有技巧!

思路

当遍历第i个元素的时候,看之前遍历的元素集合中是否存在target-nums[i],如果存在则返回两个下表,否则继续往下遍历
显而易见,用哈希法
又因为找的是元素值,返回的是下标,有两个元素,因此用map(key,val),
因为要通过元素值来判断,因此key存储元素值,返回的是元素值的下标,因此val存储下标

代码

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        std::unordered_map <int,int> map;
        for(int i = 0; i < nums.size(); i++) {
            // 遍历当前元素,并在map中寻找是否有匹配的key
            auto iter = map.find(target - nums[i]); 
            if(iter != map.end()) {
                return {iter->second, i};
            }
            // 如果没找到匹配对,就把访问过的元素和下标加入到map中
            map.insert(pair<int, int>(nums[i], i)); 
        }
        return {};
    }
};

注意map的一些操作写法
2023.08.16补充:map也可以像数组一样写,例如map[key]=value,代码可见day7的第一题

今日收获

  1. 需要掌握set和map的一些基础操作方式
  2. 写代码前先理清逻辑,不要急这写
  3. 当我们需要查询一个元素是否出现过,或者一个元素是否在集合里的时候,就要第一时间想到哈希法

你可能感兴趣的:(算法,散列表,数据结构)