代码随想录day06

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

242.有效的字母异位词

题目链接/文章讲解/视频讲解: 代码随想录

class Solution {
public:
    bool isAnagram(string s, string t) {
        int hash[26]={0};
        for(int i=0;i<s.size();i++)
        {
            hash[s[i]-'a']++;
        }
        for(int j=0;j<t.size();j++)
        {
            hash[t[j]-'a']--;
        }
        for(int i=0;i<25;i++)
        {
            if(hash[i]!=0)
            {return false;}
        }
        return true;
    }
};

注意点:

 hash[s[i]-'a']++;为哈希映射,只需要将 s[i] - ‘a’ 所在的元素做+1 操作即可,并不需要记住字符a的ASCII,只要求出一个相对数值就可以了。 这样就将字符串s中字符出现的次数,统计出来了。

  • 时间复杂度:O(n + m),其中 nm 是两个字符串的长度。(遍历s,为每一个字符访问一遍,数组一次访问的时间复杂度为O(1),访问n次,为O(n),遍历字符串t同理)
  • 空间复杂度:O(1)。
  • 哈希表 hash
    哈希表 hash[26] 是一个固定大小的数组,用来记录每个字母的出现次数。由于数组的大小是固定的 26(表示小写字母 'a' 到 'z'),所以这一部分的空间复杂度是 O(1)。

关于hash[26]空间复杂度为O(1)的问题:如果使用数据的大小与输入数据的规模有关,例如,你在处理一个长度为 n 的字符串,并且需要一个数组来记录每个字符的出现次数(如 hash[n]),那么空间复杂度会是 O(n),因为数组的大小是动态变化的,取决于输入的大小。

但是,如果你使用的是一个大小固定的数组(数组的长度不会变化),如 hash[26],那么无论输入的数据量如何变化,这个数组的大小始终不变,因此空间复杂度为 O(1)

349. 两个数组的交集

1.建议使用set,(两个数组其中的数差值较大,数量较多)

题目链接/文章讲解/视频讲解:代码随想录

哈希数据结构:unordered_set(已经进行数组去重操作),无序(效率更高)

set(自动排序,升序)

注意题目特意说明:输出结果中的每个元素一定是唯一的,也就是说输出的结果的去重的, 同时可以不考虑输出结果的顺序

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int>result;//存放两数组交集的结果
        unordered_set<int>nums01(nums1.begin(),nums1.end());
        for(int nums:nums2)
        {
            if(nums01.find(nums)!=nums01.end())
            {
                result.insert(nums);
            }
        }
        return vector<int>(result.begin(),result.end());
    }
};

注意点:find(10) != s.end() 判断了 set 是否包含元素 10。如果 find 返回的迭代器不是 end(),说明 10 存在于 set 中。

  • std::set 是一个集合容器,自动按升序(默认)排列,且不允许重复元素。
  • 常见的操作包括插入、查找、删除、遍历、获取大小等。
  • set 中的元素都是唯一的,且内部自动排序,因此查找、插入、删除等操作的时间复杂度通常是 O(log n)。

    2.直接采用数组

    class Solution {
    public:
        vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
            unordered_set<int>result;//存放两数组交集的结果
            int hash[1005]={0};
           for(int nums:nums1)//遍历num1中所有的nums
           {
            hash[nums]=1;
           }
           for(int nums:nums2)
           {
            if(hash[nums]==1)
            {result.insert(nums);}
           }
           return vector<int>(result.begin(),result.end());
        }
    };

    比较:直接使用set 不仅占用空间比数组大,而且速度要比数组慢,set把数值映射到key上都要做hash计算的。

202. 快乐数 (使用set)

题目链接/文章讲解:代码随想录

题目中说了会 无限循环,那么也就是说求和的过程中,sum会重复出现,这对解题很重要!

可采用unordered_set,底层逻辑为哈希实现,不能重复的数组,可用来判断sum是否会重复出现,unordered_set用来存储已经出现的sum值。

class Solution {
public:
        int getsum(int n)
        {
            int sum=0;
            while(n)
            {
                sum+=(n%10)*(n%10);
                n=n/10;
            }
            return sum;
        }

         bool isHappy(int n) {
             unordered_set<int>s;//不能重复的数组,判断是否无限循环,因为循环一定重复
        while(1)
        {
            int sum=getsum(n);
            if(sum==1){return true;}
                if(s.find(sum)!=s.end())
                {
                    return false;
                }
                else {s.insert(sum);}
                n=sum;//更新sum的值  
        }
    }
};

1. 两数之和 (map解决)

题目链接/文章讲解/视频讲解:代码随想录

1.什么时候可以使用哈希法,当我们需要查询一个元素是否出现过,或者一个元素是否在集合里的时候,就要第一时间想到哈希法。本题呢,我就需要一个集合来存放我们遍历过的元素,然后在遍历数组的时候去询问这个集合,某元素是否遍历过,也就是 是否出现在这个集合。

2.此时就要选择另一种数据结构:map ,map是一种key value的存储结构,可以用key保存数值,用value再保存数值所在的下标。

std::pairstd::map 中非常常用,因为 std::map 是一个键值对容器,每个元素都由一个 pair 组成,其中:

  • first 是键(Key)。
  • second 是值(Value)。

3.这道题目中并不需要key有序,选择std::unordered_map 效率更高! 

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
           unordered_map<int,int>m;//存储已经遍历的数组元素
          for(int i=0;i<nums.size();i++)
          {
            int need=target-nums[i];
            auto iter=m.find(need);//返回的是一个迭代器
            if(iter!=m.end())
            {
                return {iter->second,i};
            }
            else
            {
                m.insert(pair<int,int>(nums[i],i));//若没有在已有的存储容器中map找到相应的值,则将数值插入遍历容器中
            }
          }
return{};     
        }
};

return {}; 表示返回一个空的 std::vector<int>

return {}; 不能存在于 for 循环的括号内,是因为 return 语句会立即结束函数的执行,导致循环不再继续执行。所以,若将 return {}; 放入循环体内,它会在第一次执行时就退出函数,跳过后续的代码。

具体原因:

  1. return 语句的作用

    • return 用来退出函数并返回一个值。无论 return 语句在函数体中的哪个位置被执行,都会立即终止函数的执行,不会再执行任何后续代码。
  2. for 循环和 return

    • 如果 return {};for 循环的括号内(即循环体内),当第一次执行到 return {}; 时,整个函数会立即返回,不会继续执行后续的循环迭代。
    • 这意味着如果你在循环中写 return {};,在任何一次循环中,函数就会提前结束,导致无法完成对数组中所有元素的遍历。
    • return {}; 只能放在函数末尾return {}; 应该放在 for 循环外面,当遍历完整个数组后,仍未找到匹配的元素时,函数才会返回空的 std::vector<int>

二.采用双指针法解决(确保数组已经排序)

排序后,索引信息会被打乱,为了保持原数组的索引信息,你可以在排序时保持元素的原始索引。你可以使用 pair<int, int> 来存储元素和它的索引。排序时根据元素的值排序,但仍然保留元素的原始位置。

sort(indexed_nums.begin(), indexed_nums.end()); 语句会对 indexed_nums 中的元素进行排序,默认是按 pair 中的第一个元素(即数组中的值)排序。

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        // 创建一个存储元素及其索引的数组
        vector<pair<int, int>> indexed_nums;
        for (int i = 0; i < nums.size(); i++) {
            indexed_nums.push_back({nums[i], i});
        }
        
        // 按照元素值排序
        sort(indexed_nums.begin(), indexed_nums.end());

        int index1 = 0;
        int index2 = indexed_nums.size() - 1;

        while (index1 < index2) {
            int sum = indexed_nums[index1].first + indexed_nums[index2].first;
            if (sum == target) {
                // 返回原始索引
                return {indexed_nums[index1].second, indexed_nums[index2].second};
            } else if (sum < target) {
                index1++;
            } else {
                index2--;
            }
        }
        
        return {}; // 如果没有找到,返回空
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值