数据结构算法刷题--哈希表

1、哈希表理论基础

  • hash table :哈希表、散列表
  • 哈希表是根据关键码的值而直接进行访问的数据结构。
  • 常见的三种哈希结构:数组、set(集合)、map(映射)
  • c++中std::unordered_set底层实现为哈希表,std::set 和std::multiset 的底层实现是红黑树,红黑树是一种平衡二叉搜索树,所以key值是有序的,但key不可以修改,改动key值会导致整棵树的错乱,所以只能删除和增加。——对应java中 HashSet、TreeSet
  • c++中std::unordered_map 底层实现为哈希表,std::map 和std::multimap 的底层实现是红黑树。同理,std::map 和std::multimap 的key也是有序的。——对应java中有 HashMap、TreeMap
  • 当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。

2、有效的字母异位词

  • 题目:https://leetcode.cn/problems/valid-anagram/
  • 思路:字母只会有26个,使用数组哈希,以出现过的字符与字符’a’的数值差为索引;遍历第一个字符串中每个字符,对应数组哈希位置++,遍历第二个字符串–,最后遍历数组,如果有不为0的位置返回false;否则为true

3、两个数组的交集

  • 题目:https://leetcode.cn/problems/intersection-of-two-arrays/
  • 思路:输出结果中每个元素是唯一的,不重复、不考虑顺序——HashSet。先遍历数组1,将所有出现过的元素添加到HashSet,再遍历数组2,其中的元素如果在HashSet中出现就添加到结果的集合中,最后记得将结果转为数组返回。

4、快乐数

  • 题目:https://leetcode.cn/problems/happy-number/
  • 思路:替换过程可能会无限循环——将所有出现过的结果添加到HashSet中,只要结果不是目标1就进行操作,每次替换操作之前判断当前要替换的数是否出现过,出现过直接false;没出现过加到HashSet中,进行替换操作——取余、取模操作。

5、两数之和

  • 题目:https://leetcode.cn/problems/two-sum/
  • 思路:遍历每个数的时候,判断target和他的差是否出现过,如果出现过要取出那个数的索引——既要判断存不存在,又要取一个对应的值——HashMap。遍历数组中的每个数,判断target和它的差是不是在 HashMap中,存在取出索引和当前值的索引放到数组返回;否则将当前数放进HashMap,用于后面的数的判断。

6、四数相加Ⅱ

  • 题目:https://leetcode.cn/problems/4sum-ii/
  • 思路:两数之和的改进,四个整数数组元素可以重复,只求满足的结果个数;利用哈希——HashMap,先统计前两个数组可能的和当作两数之和的第一个数的情况作为key,出现次数作为value;再遍历后两个数组,计算和,求与target的差是否在map中,在取出value加到结果中。

7、赎金信

  • 题目:https://leetcode.cn/problems/ransom-note/
  • 思路:有效的字母异位词的改进,判断第二个字符串中是否包含第一个赎金信字符串的所有字符——哈希数组;先遍历杂志字符串将所有字符加到哈希数组中,字符和字符 ‘a’ 的差为索引,字符出现的次数为值;然后遍历赎金信字符串,遍历到的字符数组值减一;最后遍历哈希数组记录,有负值返回false,否则为true。

8、三数之和

  • 题目:https://leetcode.cn/problems/3sum/
  • 思路:和四数相加Ⅱ的不同在于,返回的每种满足要求的结果不能重复出现,所以如果使用哈希的话,存在的一个问题就是要去重,操作很复杂。这里采用双指针!!!
    • 先对数组进行排序,方便后面的去重以及剪枝操作;
    • 三个数,第一个数遍历数组每次固定,然后后面两个数采用双指针,第一个指针从第一个数后面一位开始从前往后,第二个指针从最后一个数开始从后往前。
    • 对于每一个数都要有去重操作,避免结果重复。
    • 第一个数还要进行剪枝,因为排过序,target是0,所以只要它大于0,直接结束break掉,去重操作是如果它和它的前一个数一样(注意要i > 0),这样判断出来的结果就算满足也和上一个一样,直接continue掉;
    • 然后双指针来确定后面两个数,计算三个数的和,如果大于0,右指针大了,减;如果小于0,左指针小了,加;否则满足要求,加入到结果中,此时要注意对后两个数去重,如果左指针数和它的下一个数相同,左指针加去重;右指针数和它的前一个数相同,右指针减去重;最后记得,左右指针要各自再多走一步,走出当前数。

9、四数之和

  • 题目:https://leetcode.cn/problems/4sum/
  • 思路:三数之和的扩展——双指针法,先通过两层遍历确定前两个数,后两个数通过双指针确定
    • 第一个数的剪枝去重:剪枝要注意这里的target不为0,剪枝条件为: if(nums[i] > target && nums[i] > 0 && target > 0) ,去重同三数之和;
    • 第二个数从第一个数的下一位开始遍历,剪枝条件为 if(nums[j] > target - nums[i] && nums[i] > 0) ,去重也是判断它和它前一个数(注意要 j > i);
    • 双指针操作判断结果以及去重与三数之和相同
    • 总的时间复杂度 O(n^3)
    • 同理,五数之和、六数之和…都可以这样采用双指针,外面是 n - 2 层循环嵌套。

10、总结

  • 一般来说哈希表都是用来快速判断一个元素是否出现集合里。
  • 哈希函数、哈希碰撞:
    • 哈希函数是把传入的key映射到符号表的索引上。
    • 哈希碰撞处理有多个key映射到相同索引上时的情景,处理碰撞的普遍方式是拉链法和线性探测法。
  • 三种哈希结构:数组、set(集合)、map(映射)

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