【数据结构】哈希表

文章目录

  • 哈希表的定义
  • 常见的哈希结构
    • set
    • map
  • leetcode242.有效的字母异位词
  • leetcode383.赎金信
  • leetcode349.两个数组的交集
  • leetcode350.两个数组的交集Ⅱ
  • leetcode202.快乐数
  • leetcode1.两数之和
  • leetcode15.三数之和

哈希表的定义

哈希表是根据关键码的值而直接进行访问的数据结构。
给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希表,函数f(key)为哈希函数

哈希表通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。
它牺牲了空间换取了时间,因为要使用额外的数组、set或map来存放数据,才能实现快速的查找。

当我们要快速判断一个元素是否出现在集合里时,就要考虑哈希法。

常见的哈希结构

常见的三种哈希数据结构:数组、set、map。
在C++中,set和map又分别被提供了三种数据结构。

set

std::unordered_set底层实现为哈希表,std::setstd::multiset 的底层实现是红黑树,红黑树是一种平衡二叉搜索树,所以key值是有序的,但key不可以修改,改动key值会导致整棵树的错乱,所以只能删除和增加。

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

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

map

map 是一个key-value的数据结构,map中对key有限制,对value没有限制,因为key是使用红黑树存储的。
std::unordered_map 底层实现为哈希表,std::mapstd::multimap 的底层实现是红黑树。同理,std::map 和std::multimap 的key也是有序的。

映射 底层实现 是否有序 数值是否可以重复 能否更改数值 查询效率 增删效率
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)

leetcode242.有效的字母异位词

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。

class Solution {
public:
    bool isAnagram(string s, string t) {
        if(s.size() != t.size()) {
            return false;
        }
        int record[26] = {0};
        for(int i = 0; i < s.size(); i++) {
            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) {
                return false;
            }
        }
        return true;
    }
};

leetcode383.赎金信

给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。如果可以,返回 true ;否则返回 false 。
magazine 中的每个字符只能在 ransomNote 中使用一次。

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        if(ransomNote.size() > magazine.size()) {
            return false;
        }
        int record[26] = {0};
        for(int i = 0; i < magazine.size(); i++) {
            record[magazine[i] - 'a']++;
        }
        for(int i = 0; i < ransomNote.size(); i++) {
            record[ransomNote[i] - 'a']--;
            if(record[ransomNote[i] - 'a'] < 0) {
                return false;
            }         
        }
        return true;
    }
};

leetcode349.两个数组的交集

给定两个数组 nums1 和 nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序。

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> resultSet;
        unordered_set<int> tmpSet(nums1.begin(), nums1.end());
        for(int num : nums2) {
            if(tmpSet.find(num) != tmpSet.end()) {
                resultSet.insert(num);
            }
        }
        return vector<int>(resultSet.begin(), resultSet.end());
    }
};

leetcode350.两个数组的交集Ⅱ

给你两个整数数组 nums1 和 nums2 ,请你以数组形式返回两数组的交集。返回结果中每个元素出现的次数,应与元素在两个数组中都出现的次数一致(如果出现次数不一致,则考虑取较小值)。可以不考虑输出结果的顺序。

class Solution {
public:
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
        unordered_map<int, int> tmpMap;
        vector<int> result;
        for(int num : nums1) {
            tmpMap[num]++;
        }
        for(int num : nums2) {
            if(tmpMap.count(num)) {
                result.push_back(num);
                tmpMap[num]--;
                if(tmpMap[num] == 0) {
                    tmpMap.erase(num);
                }
            }
        }
        return result;
    }
};

leetcode202.快乐数

编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」 定义为:

  • 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
  • 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
  • 如果这个过程 结果为 1,那么这个数就是快乐数。
    如果 n 是 快乐数 就返回 true ;不是,则返回 false 。
class Solution {
public:
    int getSum(int n) {
        int result = 0;
        while(n) {
            result += (n % 10) * (n % 10);
            n /= 10;
        }
        return result;
    }
    bool isHappy(int n) {
        unordered_set<int> sums;
        while(1) {
            int sum = getSum(n);
            if(sum == 1) {
                return true;
            }
            if(sums.find(sum) != sums.end()) {
                return false;
            }
            sums.insert(sum);
            n = sum;
        }
    }
};

leetcode1.两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> map;	// 存放我们已经访问过的元素
        for(int i = 0; i < nums.size(); i++) {
            auto it = map.find(target - nums[i]);
            if(it != map.end()) {
                return {it->second, i};
            }
            map.insert(pair<int, int>(nums[i], i));
        } 
        return {};
    }
};

leetcode15.三数之和

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。

// TODO

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