代码随想录算法训练营Day6 | 242.有效的字母异位词 349. 两个数组的交集 202. 快乐数 1. 两数之和

Day6

哈希表

哈希表实际上就是通过键key而直接访问值value的数据结构

哈希表(h)的表现形式如数组(v)一般

  • 在数组中新增一个元素,我们可以在尾部push_back,或者在某个位置insert
  • 在哈希表中新增一个元素,需要一个键值对(key-value pair),我们再通过计算key来得到数据value在哈希表中的具体位置,随后再插入。
  • 在数组中我们知道可以通过数组的索引来随机访问数组中的数据内容,利用v[index]即可
  • 在哈希表中我们也可以通过[]来直接访问数据,只不过[]中填入的内容是key,例如h[key]

对于数组和哈希表来讲,访问元素的时间都是O(1),因为他们都支持随机访问,即利用[]访问数据

在查询方面,哈希表比数组有优势。当我们要查找是否存在某个元素时,数组需要花O(n)的时间遍历全部才能得出结论,而哈希表只需要检查h[key]是否存在在表中就能在O(1)时间内得出结果

哈希函数

哈希函数是将我们存入的数据映射到哈希表中的函数,通过数学运算给出一个哈希表中的位置,后续我们根据这个位置才能向表中插入数据。即:它能把数据映射成哈希表上的数字索引。

在这里插入图片描述

如果hashCode得到的索引大于哈希表的大小了怎么办?

  • 为了保证映射出来的索引数值都落在哈希表上,我们会再次对数值做一个取模,这样就保证了学生姓名一定可以映射到哈希表上了。

如果学生的数量大于哈希表的大小怎么办?

  • 此时就算哈希函数计算的再均匀,也会有几位学生的名字同时映射到哈希表同一个索引的位置,此时就会发生哈希碰撞。

哈希碰撞

如图,假设小李和小王都映射到了索引下标 1 的位置,这就发生了冲突,即哈希碰撞。

在这里插入图片描述

解决碰撞一般有两种

拉链法

我们将冲突的数据存成链表的形式,这样多个数据都可以保留了

在这里插入图片描述

使用拉链法要选择适当的哈希表大小,不会因为表太大空值太多而浪费内存,也不会因为表太小全是链表而影响查询速度。(因为链表需要O(n)查找)

线性探测法

线性探测法要保证tableSize大于dataSize。 我们需要依靠哈希表中的空位来解决碰撞问题。

例如冲突的位置,放了小李,那么就向下找一个空位放置小王的信息。所以要求tableSize一定要大于dataSize ,要不然哈希表上就没有空置的位置存放冲突的数据了。

在这里插入图片描述

常见的哈希结构

  • 数组
  • set (集合)
  • map(映射)

对于集合:

集合底层实现是否有序数值是否可以重复能否更改数值查询效率增删效率
std::set红黑树有序O(log n)O(log n)
std::multiset红黑树有序O(logn)O(logn)
std::unordered_set哈希表无序O(1)O(1)

对于映射:

映射底层实现是否有序数值是否可以重复能否更改数值查询效率增删效率
std::map红黑树key有序key不可重复key不可修改O(logn)O(logn)
std::multimap红黑树key有序key可重复key不可修改O(log n)O(log n)
std::unordered_map哈希表key无序key不可重复key不可修改O(1)O(1)

当我们要使用集合来解决哈希问题的时候,优先使用unordered_set,因为它的查询和增删效率是最优的,如果需要集合是有序的,那么就用set,如果要求不仅有序还要有重复数据的话,那么就用multiset。

总结

当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法

但是哈希法也是牺牲了空间换取了时间,因为我们要使用额外的数组,set或者是map来存放数据,才能实现快速的查找。


242. Valid Anagram

class Solution {
public:
    bool isAnagram(string s, string t) {
        if(s.length() != t.length())
            return false;
        unordered_map<char, int> mp;

        for(char c : s)
        {
            ++mp[c];
        }

        for(char c : t)
        {
            --mp[c];
            if(mp[c] < 0)
                return false;
        }
        return true;
    }
};

349. Intersection of Two Arrays

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_map<int, int> mp;
        for(int i : nums1)
        {
            ++mp[i];
        }
        vector<int> ans;
        for(int i : nums2)
        {
            if(mp[i] != 0){
                ans.push_back(i);
                mp[i] = 0;
            }
        }
        return ans;
    }
};

202. Happy Number

class Solution {
public:
    bool isHappy(int n) {
        set<int> s;
        int sum = 0;
        while(n != 1)
        {
            int tmp = n;
            while(1)
            {
                int num = tmp % 10;
                sum += num * num;
                tmp = tmp / 10;
                if(tmp == 0)
                    break;
            }
            if(s.find(sum) != s.end())
                return false;
            s.insert(sum);
            n = sum;
            sum = 0;
        }
        return true;
    }
};

1. Two Sum

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> map;

        for(int i=0; i<nums.size(); ++i) {
            if(map[nums[i]] != 0) {
                return {i, map[nums[i]]-1};

            }
            map[target-nums[i]] = i+1;
        }
        return {};
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值