数据结构-哈希表-总结(简)

1.哈希表基础

一般来说哈希表都是用来快速判断一个元素是否出现集合里。

对于哈希表,要知道哈希函数和哈希碰撞在哈希表中的作用.

哈希函数是把传入的key映射到符号表的索引上。

哈希碰撞处理有多个key映射到相同索引上时的情景,处理碰撞的普遍方式是拉链法和线性探测法。

2什么时候使用哈希表以及使用哪种哈希表

当我们的思路中涉及到查找相关的步骤,可以考虑把数据放到数组,set或者map中用哈希法解决问题

2.1 数组

对于数据大小(种类)确定,可以使用一个固定大小的数组来存放和查找,且能用数组就用数组,因为简单且效率高

383.赎金信 中同样要求只有小写字母,那么就给我们浓浓的暗示,用数组!

本题和242.有效的字母异位词 很像,242.有效的字母异位词是求 字符串a 和 字符串b 是否可以相互组成,在383.赎金信中是求字符串a能否组成字符串b,而不用管字符串b 能不能组成字符串a

上面两道题目用map确实可以,但使用map的空间消耗要比数组大一些,因为map要维护红黑树或者符号表,而且还要做哈希函数的运算。所以数组更加简单直接有效!

2.2 set

首先要了解数组的局限:

  • 数组的大小是有限的,受到系统栈空间(不是数据结构的栈)的限制。
  • 如果数组空间够大,但哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费。

当给出的数据大小不确定,且只需要一个值,用set

关于set,C++ 给提供了如下三种可用的数据结构:(详情请看关于哈希表,你该了解这些! (opens new window))

  • std::set
  • std::multiset
  • std::unordered_set
    std::set和std::multiset底层实现都是红黑树,std::unordered_set的底层实现是哈希,使用unordered_set 读写效率是最高的,本题并不需要对数据进行排序,而且还不要让数据重复,所以选择unordered_set。(还没有使用过其他set)

2.3 map

数组和set的局限:

  • 数组的大小是受限制的,而且如果元素很少,而哈希值太大会造成内存空间的浪费。
  • set是一个集合,里面放的元素只能是一个key,而两数之和这道题目,不仅要判断y是否存在而且还要记录y的下标位置,因为要返回x 和 y的下标。所以set 也不能用。

如果需要存放两个值,且大小未知,则用map

C++提供如下三种map::(详情请看关于哈希表,你该了解这些! (opens new window))

  • std::map
  • std::multimap
  • std::unordered_map

std::unordered_map 底层实现为哈希,std::map 和std::multimap 的底层实现是红黑树。

同理,std::map 和std::multimap 的key也是有序的(这个问题也经常作为面试题,考察对语言容器底层的理解),1.两数之和 中并不需要key有序,选择std::unordered_map 效率更高!

3.常见使用方式

3.1 数组:

把数据值存放在数组的下标中,数组元素用来对表示对下标的操作的结果

record[magazine[i]-'a'] ++;
 record[s[i] - 'a']++;

3.2 set

将数据存放到set中,然后使用find查找

1:多组数据

定义两个set,一个set_1用于数据存放到其中,另一个set_2用于数据挨个到set_1中查找,查找结果放到set_2中

		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);
            }
        }

2:单组数据:

定义一个set,边判断边存入,先判断(查询)是否符合条件,符合条件就return,不符合的就存入set中

			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;

3.3 map

1:存放数据并查找
先判断是否满足条件,没找到匹配的,就把数据存入map中

// 遍历当前元素,并在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)); 

2:先用key存放数据,value存放对这个数据操作的结果;然后使用find进行查询

		unordered_map<int, int> umap; //key:a+b的数值,value:a+b数值出现的次数
        // 遍历大A和大B数组,统计两个数组元素之和,和出现的次数,放到map中
        for (int a : A) {
            for (int b : B) {
                umap[a + b]++;
            }
        }
        int count = 0; // 统计a+b+c+d = 0 出现的次数
        // 在遍历大C和大D数组,找到如果 0-(c+d) 在map中出现过的话,就把map中key对应的value也就是出现次数统计出来。
        for (int c : C) {
            for (int d : D) {
                if (umap.find(0 - (c + d)) != umap.end()) {
                    count += umap[0 - (c + d)];
                }
            }
        }

4.本章题目思路总结

1:

用数组当成哈希表,数据当作下标,把nums[i]用于计数,先计第一个string,用++,再计第二个string,用- -;

2:

涉及两组数据,定义两个set,一个为result,一个set放其中一组数据,把查找set中有无和另一组数据相同的数据,把相同的放到result中

3:

定义一个set,先判断sum是否再result中出现过,然后再把sum放进result中

4:

定义一个map,遍历,先判断目标元素是否在map中,在的话返回结果,不在的话就把当前nums[i]和下标存入map中

5:

涉及到四组数据,按照之前的思路,应该用三层for循环,最后一层查找,但是这样时间复杂度,空间复杂度(要先复制三组数据)都比较高,由于本题要求的答案只是次数的统计,所以没必要按照这样的完整方案实行:
定义一个map,key存放a+b的值,value存放对应值出现的次数,然后再次遍历,将map[0-c-d]的数量加到count上, 最后输出count;
本题算是一种小技巧,和其他题有点区别

6:

用数组,先再magazine中把每一个字母映射到一个数组record中,具体方法:下标存字母对应顺序,数值存字母出现次数,然后再ransom note中把字母对应的下标中的数值- -,并且在此轮循环中就可以判断是否出现record【i】<0(magazine中没有的字母但ransom note中出现了),出现了就return false,一直没出现就return true;

7,8:

用哈希表很复杂(去重麻烦),双指针方便

要点总结:

1:一般为减少时间开支,在最后一个for循环里面,一边遍历,判断存放数据,一边就判断时候输出,能少用一个循环
2:待定(二刷说不定有新体会)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值