哈希表算法题

1.两数之和

因为数组是无序的,所以我们无法直接使用双指针。如果排序了,下标映射就变了,还得需要存储下标比较麻烦,所以这里我们采取反向暴力的解法:

解法1:反向暴力 

         找两个数之和为target,我们可以固定一个数x,然后在其前面找有没有一个数等于target-x。如果没有,我们x就走到下一个位置。然后重复找前面有没有一个数等于target-x的过程。

这个算法是否正确呢?答案是肯定的。因为如果我们从后往前看的话,就知道,其实就相当于是个反向的暴力。

时间复杂度:O(n^2),本质上还是暴力解法

空间复杂度:O(1)

vector<int> __twoSum(const vector<int>& nums, int target)
{
    int n = nums.size();
    for(int i=1; i<n; ++i)
    {
        for(int j=i-1; j>=0; j--)
        {
            if(nums[i] + nums[j] == target) return {i, j};
        }
    }
    return {};
}

解法2:反向暴力+哈希表

        我们在反向遍历的过程中,一直在找该数的前面有没有target-x的这个数出现过。所以我们可以利用hash表,来快速查找某个数是否存在。当我们遍历完一个数之后,就将数和其下标放入到哈希表中,这样遍历到下一个数时,它前面的元素都在hash表中了,就可以快速查看所需要的内容是否存在。 

时间复杂度:O(n),遍历一遍数组,在哈希表中查找某个数是否存在是O(1)

空间复杂度:O(n),最坏情况下,需要将n-1个数全部放入到哈希表中。

vector<int> twoSum(const vector<int>& nums, int target)
{
    int n = nums.size();
    unordered_map<int, int> hash;
    for(int i=0; i<n; ++i)
    {
        int key =  target - nums[i];
        if(hash.count(key)) return {hash[key], i};
        hash[nums[i]] = i;
    }
    return {};
}

2.判断是否互为字符重排 

 

判断是否互为字符重排其实就是判断两个字符串排序后是否相等。但是如果用了排序,时间复杂度就高了,这里我们采用哈希表,以空间来换时间。

解法:哈希表

        因为字符串只包含小写字母,所以我们可以创建一个大小为26的数组来模拟哈希表。接着我们先遍历s1将其字符出现的个数记录在hash表中,接着遍历s2,将其出现的字符在哈希表中减去。如果最后哈希表中全都是0,则表明互为字符重排,反之则不是。

时间复杂度:O(n)

空间复杂度:O(26)

class Solution 
{
    int hash[26] = { 0 };
public:
    bool CheckPermutation(string s1, string s2) 
    {
        if(s1.size() != s2.size()) return false;
        
        for(auto ch : s1) hash[ch-'a']++;
        for(auto ch : s2) hash[ch-'a']--;
        for(auto e : hash)
            if(e)
                return false;
        return true;
    }
};

3.存在重复元素

题目还是很简单的,只要数组中有重复元素就返回ture,反之返回false。

解法:哈希表

        我们可以遍历数组,将其每个元素放入到哈希表中,在放入之前先判断该元素在哈希表中出现过没,如果出现了,那就返回ture,如果遍历完了数组也没有返回,则说明没有重复元素,返回false。

时间复杂度:O(n),最坏情况下需要遍历一遍数组

空间复杂度:O(n)

class Solution 
{
public:
    bool containsDuplicate(vector<int>& nums) 
    {   
        unordered_set<int> hash;
        for(auto e : nums) 
        {
            if(hash.count(e))
                return true;
            hash.emplace(e);
        }
        return false;
    }
};

4.存在重复元素2

 题目要求在数组中找到两个重复的数,且这两个数的下标差值的绝对值需要小于等于k。如果能找到这样重复的数,则返回true;反之返回false。

解法:反向+哈希表

        我们依旧可以找一个数的前面是否有一个相同的数,且下标差值小于k。并且这样找的话我们就可以规避掉求绝对值的过程。因为我们向前找的时候,前面的下标肯定是小于当前位置的下标的。而且如果数组中如果有多个重复的元素,该解法会天然的将哈希表中已存在的数的下标更新到离当前元素最近的位置。

时间复杂度:O(n)

空间复杂度:O(n)

class Solution 
{
public:
    bool containsNearbyDuplicate(vector<int>& nums, int k) 
    {
        unordered_map<int,int> hash;
        for(int i=0; i<nums.size(); ++i)
        {
            if(hash.count(nums[i]) && i-hash[nums[i]] <= k)
                return true;
            hash[nums[i]] = i;
        }
        return false;
    }
};

5.字母异位词分组

 解法:哈希表

        我们可以对每一个字符串进行排序,排序后如果相等就是字母异位词。接着我们利用unordered_map<string, vector<string>> hash来对同一个字母异位词进行分组。是字母异位词的会被放入到同一个vector中。最后我们只需要遍历hash表,将value存储下来即可。

时间复杂度:O(nklogk),n是数组的长度,k是字符串的最大长度

空间复杂度:O(nk),n是strs的长度,k是strs中最大字符串长度。

class Solution 
{
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) 
    {
        unordered_map<string,vector<string>> hash;
        for(auto& e : strs)
        {
            string tmp = e;
            sort(tmp.begin(),tmp.end());
            hash[tmp].emplace_back(e);
        }
        vector<vector<string>> ret;
        for(auto e : hash)
            ret.emplace_back(e.second);
        return ret;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值