Leetcode——哈希表

什么时候使用哈希法?当需要查询一个元素是否出现过,或者一个元素是否在集合里的时候,就要第一时间想到哈希法。

第1题 求两数之和

        解题思路:不用双重循环暴力解法,用哈希表试一试。为什么选用哈希表?解题关键在于一个元素是否在集合中。为什么选用map?因为既要存储元素,又要存元素下标。

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        //哈希表实现
        //可以无序,所以用unordered_map
        unordered_map<int, int> table;
        for (int i = 0; i < nums.size(); i++)
        {
            auto iter = table.find(target - nums[i]);//例如当前num[i]=2,就要在列表里找是否有7在
            if (iter != table.end())//若有,则输出,没有的话指针会走到表尾
            {
                return{ iter->second,i };//返回两个下标索引,因为存在唯一答案,直接输出就行
            }
            //没找到的话就把当前元素以及下标存入表中
            table.insert(pair<int,int>(nums[i], i));//pair是将2个数据组合成一组数据
        }
        return {};
    }
};

第4题 四数相加Ⅱ

        解题思路:跟第一题解法相似,可以将四个数组两两结合。这里要存储的是求和为0出现的次数,所以使用unordered_map。

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        //key是数据求和的值,value是这个和出现的次数
        unordered_map<int, int> table;
        int count = 0;//用于计数
        int n = nums1.size();
        //将四个数组分为两两一组
        for (int i = 0; i < n; i++)
            for (int j = 0; j < n; j++)
                table[(nums1[i] + nums2[j])]++;
        for (int k = 0; k < n; k++)
            for (int l = 0; l < n; l++)
                if (table.find(0 - (nums3[k] + nums4[l])) != table.end())
                    count += table[0 - (nums3[k] + nums4[l])];
        return count;
    }
};

第15题 三数相加 

        解题思路:这道题哈希表做就会变得复杂化。此处算法参考的是leetCode的精选题解。

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        //特殊情况直接判断结束
        if (nums.size() < 3)
            return {};
        //将数组排序
        sort(nums.begin(), nums.end());
        int i = 0;
        vector<vector<int>> res;
        while (i < nums.size())
        {
            if (nums[i] > 0)
                break;//当前第一个数字大于零,直接提前终止循环
            int left = i + 1, right = nums.size() - 1;
            while (left < right)
            {
                int x = nums[i];
                int y = nums[left];
                int z = nums[right];
                if (x + y > 0 - z)//当前三个数字和大于0,左移right找一个小一点的数
                    right--;
                else if (x + y < 0 - z)//当前三个数字和小于0,右移left找一个大一点的数
                    left++;
                else
                {
                    //三数相加和为0,存储当前数字
                    res.push_back({x,y,z});
                    //移动指针,注意要去掉重复元素
                    while (left < right && nums[left] == nums[left + 1])
                        left++;
                    while (left < right && nums[right] == nums[right - 1])
                        right--;
                    //去掉重复元素之后在移动左右指针
                    left++;
                    right--;
                }
            }
            //同样的去重操作
            while (i + 1 < nums.size() && nums[i] == nums[i + 1])
                i++;
            i++;
        }
        return res;
        }
};

 第242题 有效的字母异位词


        解题思路:数组是形式最简单的哈希表,索引是key,存储内容是value。本题采用一维数组进行记录,因为题目中给的都是小写字母,所以定义一个长度为26的数组,通过遍历s记录每个字母出现的次数,然后再遍历t,t中出现的字母做减法记录,一正一负互相抵消,若s和t是异位词,则数组最终恢复全零。 

class Solution {
public:
    bool isAnagram(string s, string t) {
        //简单版哈希表——数组
        //定义一个数组,数组长度为26,用于记录每个字母出现的次数
        int letter[26] = {0};
        //记录s中字母出现的次数
        for(int i = 0;i<s.size();i++)
        {
            letter[s[i]-'a']++;//索引0即代表字母a
        }
        //t中出现的字符做减法计数
        for(int i = 0;i<t.size();i++)
        {
            letter[t[i]-'a']--;
        }
        //若是字母异位词,则数组最终都是0
        for(int i = 0;i<26;i++)
        {
            if(letter[i] != 0)
                return false;
        }
        return true;
    }
};

第49题  字母异位词分组

        解题思路:参考的是题解评论中的一种解决方法。主要的算法流程就是定义一个哈希表,这个哈希表的用处就是将异位词挂在同一个键值之下,这里面判断异位词的方法是将一个单词按照a~z的顺序重新生成新的键值,例如单词“eat”的键值就是“aet”,同样,单词“tea”的键值也是“aet”,它俩都会挂在key="aet"下面。最后,再将哈希表的value输出。

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
    //定义一个哈希表map,负责将给定的字符串进行分类
    map<string, vector<string>> table;
    //定义一个容器负责存储输出结果
    vector<vector<string>> res;
    for (int i = 0; i < strs.size(); i++)
    {
        string key = strs[i];//key存储的是单个字符串
        sort(key.begin(), key.end());//对单个字符串进行排序,例如“eat排序完为aet”
        table[key].push_back(strs[i]);//将排序完相同的字母排序存入同一key值下
    }
    /*auto是用来自动推导表达式或变量的实际类型的
      就如同下面的例子,table是一个很复杂的变量,
      在程序庞大或者复杂的时候,
      不知道table.begin()的返回结果是什么进而导致不确定ite前面的要输入类型应该是什么,
      通过auto关键字就能直接推到ite的数据类型,在复杂程序中很好用*/
    for (auto ite = table.begin(); ite != table.end(); ite++)
        res.push_back(ite->second);//ite->first是键值,ite->second是键值下面挂的一串数组,所以直接就能写入到res中
    return res;
    }
};

第438题  找到字符串中所有字母异位词

        解题思路:刚开始想用49题的思想去求解,但是由于测试用例过于庞大,程序执行超过时间限制,因此只能放弃。这里采用一下官方的解题思路。

vector<int> findAnagrams(string s, string p) {
        int p_len = p.size();
        int s_len = s.size();
        if (p.size() > s.size())
            return {};
        vector<int> res;//存储结果的容器
        vector<int> count_s(26);//用于计数的容器
        vector<int> count_p(26);
        for (int i = 0; i < p_len; i++)
        {
            //统计滑动窗口中每个字母出现的情况
            count_s[s[i] - 'a']++;
            count_p[p[i] - 'a']++;
            //若conut内全是0,说明当前滑动窗口与p是异位词
        }
        if (count_s == count_p)
            res.push_back(0);
        //从这里开始移动窗口
        for (int i = 0; i < s_len - p_len; i++)
        {
            //将窗口前面的字符删去计数
            count_s[s[i] - 'a']--;
            //将后一个字符加入窗口计数
            count_s[s[i + p_len] - 'a']++;

            //判断当前窗口是否是异位词
            if (count_s == count_p)
                res.push_back(i+1);
        }
        return res;
    }

第383题 赎金信 

        解题思路:同样采用上述思想,将一维数组作为哈希表来使用。遍历ransomNote,记录每个字母出现得次数,然后再遍历magazine,若也出现了相同的字母,则数组中相应的字母记录减一(对应字母可以多余ransomNote,但是不能少),若ransomNote中的字母在magazine全部出现,数组最终所有元素为0。

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        //特判
        int r_len = ransomNote.size();
        int m_len = magazine.size();
        if (r_len <= 0 || m_len <= 0)
            return false;
        if (r_len > m_len)
            return false;
        int letter[26] = { 0 };//定义一个数组存储ransomNote中字母出现得个数
        for (int i = 0; i < r_len; i++)
            letter[ransomNote[i] - 'a']++;
        for (int j = 0; j < m_len; j++)
        {
            if (letter[magazine[j] - 'a'] > 0)
                letter[magazine[j] - 'a']--;
        }
        //若letter全为零,说明magazine里包含组成ransomNote全部字母
        int sum = accumulate(letter, letter + 26, 0);
        if (sum)
            return false;
        else
            return true;
    }
};

第349题  两个数组的交集

        解题思路:这是一道简单题,主要的算法思想就是遍历第一个数组,记录一下num1中有哪些数字出现过,再遍历第二个数组,看看num2中哪些数字在1中出现过,然后记录到结果res中,其中最关键的部分在于去除重复数字,因此考虑使用"unordered_set"容器。

        简单介绍以下"unordered_set"容器:

  • unordered_set 容器提供了和 unordered_map 相似的能力,但 unordered_set 可以用保存的元素作为它们自己的键;
  • 不能存放重复元素;
  • 不会对内部存储的数据进行排序。
class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        //遇见需要去重操作的,考虑set,unordered_set
        unordered_set<int> res;
        int hash[1005] = {0};//一维数组也是哈希表
        for(int i = 0;i<nums1.size();i++)
            hash[nums1[i]] = 1;//将出现过的数字标注1
        for(int i = 0;i<nums2.size();i++)
        {
            //如果num2中的元素在num1中出现过,则将其存入res中
            if(hash[nums2[i]] == 1)
                res.insert(nums2[i]);
        }
        return vector<int>(res.begin(),res.end());//强制类型转换
        
    }
};

第350题  两个数组的交集Ⅱ

        解题思路:这里不需要对数据进行去重,所以普通容器即可,整体思想与上述题目差别不大。

class Solution {
public:
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
        //没有去重操作,可以直接用容器
        vector<int> res;
        int hash[1005] = { 0 };//一维数组也是哈希表
        for (int i = 0; i < nums1.size(); i++)
            hash[nums1[i]]++;//将出现过的数字计数
        for (int i = 0; i < nums2.size(); i++)
        {
            //如果num2中的元素在num1中出现过,则将其存入res中
            if (hash[nums2[i]])//因为要输出重复元素
            {
                res.push_back(nums2[i]);
                hash[nums2[i]]--;//两个数组有重复元素,哈希表计数减一
            }
        }
        return res;
    }
};

第202题  快乐数

        解题思路:判断一个数到底是不是快乐数,一是看最后sum是否为1 ,若sum最后达不到1,如何跳出循环呢?方法就是判断当前的sum是否是第二次出现,若是,说明不是快乐数,跳出循环即可。

class Solution {
public:
    //先写一下对各位数求和函数
    int Getsum(int n)
    {
        int s=0;
        while(n)
        {
            s += (n%10)*(n%10);
            n = n/10;
        }
        return s;
    }
    bool isHappy(int n) {
        unordered_set<int> table;
        while (1)
        {
            int sum = Getsum(n);//对当前数字求各位和
            if (sum == 1)
                return true;
            else if (table.find(sum) != table.end())//当前的sum是第二次出现
                return false;
            else//既不是1也不是二次出现的数,存入表中
                table.insert(sum);
            n = sum;
        }
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值