【leetcode_C++_哈希表_day5】242. 有效的字母异位词&&349. 两个数组的交集&&202.快乐数&&1. 两数之和

C++知识补充:(不完全,仅针对本题用的知识点)

1.C++类 & 对象

img

关键字 public 确定了类成员的访问属性。在类对象作用域内,公共成员在类的外部是可访问的。您也可以指定类的成员为 privateprotected,这个我们稍后会进行讲解。

2.C++ STL

C++ STL(标准模板库)是一套功能强大的 C++ 模板类,提供了通用的模板类和函数,这些模板类和函数可以实现多种流行和常用的算法和数据结构,如向量、链表、队列、栈。

C++ 标准模板库的核心包括以下三个组件:

组件 描述
容器(Containers) 容器是用来管理某一类对象的集合。C++ 提供了各种不同类型的容器,比如 deque、list、vector、map 等。
算法(Algorithms) 算法作用于容器。它们提供了执行各种操作的方式,包括对容器内容执行初始化、排序、搜索和转换等操作。
迭代器(iterators) 迭代器用于遍历对象集合的元素。这些集合可能是容器,也可能是容器的子集。
#include <iostream>
#include <vector>//首先要添加头文件
using namespace std;
 
int main()
{
   // 创建一个向量存储 int
   vector<int> vec; 
   int i;
 
   // 显示 vec 的原始大小:vec.size()
   cout << "vector size = " << vec.size() << endl;
 
   // 推入 5 个值到向量中:vec.push_back(i);
   for(i = 0; i < 5; i++){
      vec.push_back(i);
   }
 
   // 显示 vec 扩展后的大小
   cout << "extended vector size = " << vec.size() << endl;
 
   // 访问向量中的 5 个值
   for(i = 0; i < 5; i++){
      cout << "value of vec [" << i << "] = " << vec[i] << endl;
   }
 
   // 使用迭代器 iterator 访问值
   vector<int>::iterator v = vec.begin();
   while( v != vec.end()) {
      cout << "value of v = " << *v << endl;
      v++;
   }
 
   return 0;
}

3.哈希表

给一个元素判断在这个集合中是否出现过,这种场景要想到哈希表

哈希法是牺牲了空间换取了时间

根据具体问题分析用:

🔸数组:数据量小且集中

通过leetcode242. 有效的字母异位词体验哈希表的数组应用。

🔸set(集合):数据量大且分散

​ std::set

​ std::multiset

​ std::unordered_set

再来看一下使用数组和set来做哈希法的局限。

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

set是一个集合,会自动给元素排序,从小到大

头文件

#include <set>

image-20221022224713954

🔸map(映射):map是一种key value的存储结构,可以用key保存数值,用value在保存数值所在的下标。

通过leetcode1. 两数之和来体验map的使用吧!

​ std::map

​ std::multimap

​ std::unordered_map

头文件 键值对

会自动给元素排序,从小到大,如果是字符串就按照ASCLL码排序

#include <map>

image-20221022224648986

//unordered_map和unordered_set 就是不会排序的map和set。节省运行时间的时候可以用.效率更高。

//不会排序不代表会按照原来的顺序输出。会以一种随机的顺序输出。

#include <unordered_map>
#include <unordered_set>

242. 有效的字母异位词

1.题目

给定两个字符串 *s**t* ,编写一个函数来判断 *t* 是否是 *s* 的字母异位词。

注意:*s**t* 中每个字符出现的次数都相同,则称 *s**t* 互为字母异位词。

示例 1:

输入: s = "anagram", t = "nagaram"
输出: true

示例 2:

输入: s = "rat", t = "car"
输出: false

2.分析

C语言版本:注意:字符的长度判断函数:strlen

bool isAnagram(char * s, char * t){
    int  record[26]={0};

    for(int i=0;i<strlen(s);i++)
        record[s[i]-'a']++;
    for(int i=0;i<strlen(t);i++)
        record[t[i]-'a']--;    

    for(int i=0;i<26;i++)
    {
        if(record[i]!=0)
        {
            return false; 
        }
            
    }
    return true;
}

C++版本:

class Solution {
public:
    bool isAnagram(string s, string t) {
    int i;
    int record[26]={0};//数组初始化
    for(i=0;i<s.size();i++)
    {
        record[s[i]-'a']++;
    }
    for(i=0;i<t.size();i++)
    {
        record[t[i]-'a']--;
    }
    for(i=0;i<26;i++)
    {
        if(record[i]!=0)
            return false;
    }
    return true;

    }
};

349. 两个数组的交集

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

示例 1:

输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]

示例 2:

输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]
解释:[4,9] 也是可通过的

思路:

  1. 将nums1去重
  2. 用nums2的元素和nums1的去重结果进行比较,并返回和num1的元素相同的nums2元素
  3. 对找到的nums2元素再一次去重,并且返回作为结果

心得:

用哈希表解决是很简单的题目,但是前提是掌握哈希表。

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) 
    {
        unordered_set<int> result_set;//设定存放结果
        unordered_set<int> nums_set(nums1.begin(),nums1.end());//保存nums1的去重结果
        for(int num:nums2)//遍历nums2
        {
            if(nums_set.find(num)!=nums_set.end())//在nums1的去重结果中找到nums2的元素
                result_set.insert(num);//把该元素存入result_set
        }
        return vector<int>(result_set.begin(),result_set.end());//返回result_set的去重结果
    }

};

202.快乐数

编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」 定义为:

  • 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
  • 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
  • 如果这个过程 结果为 1,那么这个数就是快乐数。

如果 n快乐数 就返回 true ;不是,则返回 false

示例 1:

输入:n = 19
输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1

示例 2:

输入:n = 2
输出:false

思路:

​ 这是一个寻找1的过程。还记得哈希表的应用场景吗?给一个元素判断在这个集合中是否出现过,这种场景要想到哈希表

​ 我们需要把每一次的平方和结果做成一个集合set,然后判断两个点:1是否在这个集合中出现过-->return true;重复的元素是否在这个集合中出现过(出现了则说明进入了死循环)-->return false

判断sum是否重复出现就可以使用unordered_set。

另外一个难点就是先定义一个每个位置上的数字的平方和的函数getsum。(也不是很难啦)

心得:

要透过现象看本质。

这道题目看上去貌似一道数学问题,但是其实是在集合中找元素。

class Solution {
public:
    int getsum(int n)//先定义一个每个位置上的数字的平方和的函数getsum
    {
        int sum=0;
        while(n)
        {
            sum+=(n%10)*(n%10);
            n/=10;
        }
        return sum;
    }
    bool isHappy(int n) {
        unordered_set<int> set;
        while(1)//重复寻找过程
        {
            int sum=getsum(n);
            if(set.find(1)!=set.end()) {//找到了1即返回true
                return true;
            }
            if(set.find(sum)!=set.end()){//如果这个sum曾经出现过,说明已经陷入了无限循环了,这种情况时钟变不到1,立刻return false
                return false;
            }
            else{
                set.insert(sum);//收集这个sum结果
            }
            n=sum;
        }

    }
};

1. 两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]

思路:

本题四个重点:

  • 为什么会想到用哈希表

    以case1为例:访问2的时候就想看看集合里有没有7,访问7的时候就想找找集合里有没有2,如果找到了就可以返回了。注意本题要求返回的是下标

    本题需要一个集合来存放我们遍历过的元素,然后在遍历数组的时候去询问这个集合,某元素是否遍历过,也就是 是否出现在这个集合。

  • 哈希表为什么用map

    因为map是key value结构,可以用来存元素和其对应的下标。

    本题不涉及到排序,所以出于对效率的考虑,使用unordered_map.

心得:

​ 还是要读题,领悟。

​ 要能悟出题目是在。给一个元素,判断在这个集合中是否出现过,这种场景要想到哈希表

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

        unordered_map<int,int> map;//注意map的使用
        for(int i=0;i<nums.size();i++)
        {
            auto iter=map.find(target-nums[i]);//**补充:auto**,因为map是结构变量,为了方便就写auto
            if(iter!=map.end())//在map中找到了元素
            {
                return {iter->second,i};//返回两个下标
            }
            //如果没有找到匹配对,就把访问过的元素和下标加入到map中
            map.insert(pair<int,int>(nums[i],i));
        }
        return {};//遍历完了还没找到,就返回空
    }
};

补充:auto

auto的原理就是根据后面的值,来自己推测前面的类型是什么。

auto的作用就是为了简化变量初始化,如果这个变量有一个很长很长的初始化类型,就可以用auto代替。

posted @ 2022-10-22 22:54  只想毕业的菜狗  阅读(42)  评论(0)    收藏  举报