代码随想录算法训练营第六天| Leetcode 242.有效的字母异位词、349. 两个数组的交集、202.快乐数、1. 两数之和

Leetcode 242.有效的字母异位词

题目链接:Leetcode 242.有效的字母异位词

题目描述:给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。(s和t仅包含小写字母)

思路:万物皆可暴力,这道题也不例外,但我们还应该想一想有没有更好的方法?本道题需要判断t和s的所有字母是否相同,所以我们想到了哈希法一般哈希法都是用来快速判断一个元素是否出现集合里。

(1)我们需要将26个字母分别映射到一个数组的下标,因为字符a到字符z的ASCII是26个连续的数值,所以字符a映射为下标0,相应的字符z映射为下标25。

(2)先统计s中字母种类和数量:先找到对应下标,然后+1。之后遍历t中字母,找到对应下标,然后-1。

(3)最后检查数组,如果还存在某个位置不为0,说明s和t字母种类或个数不同,返回false。

代码如下:

class Solution {
public:
    bool isAnagram(string s, string t) {
        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;
    }
};
  • 时间复杂度: O(n)
  • 空间复杂度: O(1)

Leetcode 349. 两个数组的交集

题目链接:Leetcode 349. 两个数组的交集

题目描述:给定两个数组,编写一个函数来计算它们的交集。

思路:计算数组的交集,换句话说就是判断一个数组中的每一个元素是否存在于另一个集合中。

既然如此,我们就该想到哈希法来做这道题。由于结果需要去重,我们可以借助C++STL标准库中的set。关于set,C++ 给提供了如下三种可用的数据结构:

  • std::set
  • std::multiset
  • std::unordered_set

前两种底层原理是红黑树,存入数据后该结构自动排序;而最后一种底层原理是哈希表,该结构不会排序,因此读写数据效率最高,本题可以使用该结构。

(1)将nums1存入哈希表,unordered_set会对每个元素值生成对应的哈希值

(2)对比nums2的每个元素转换成哈希值,对比该哈希值是否存在于unordered_set,如果存在,则说明该元素为交集元素

代码如下:(使用STL库)

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> result;
        unordered_set<int> nums(nums1.begin(),nums1.end()); //先将nums1加入哈希表
        for (int num : nums2) {
            if (nums.find(num) != nums.end()) { //判断条件成立时说明num是交集元素
                result.insert(num);
            }
        }
        return vector<int>(result.begin(), result.end());
    }
};
  • 时间复杂度: O(n + m) m 是最后要把 set转成vector
  • 空间复杂度: O(n)

当然本题也可以使用数组模拟哈希表,优点是速度稍微快一些。不过对于数组元素值范围较大或数组元素个数不确定时,数组模拟就不行了。

代码如下:(数组模拟哈希表)

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> result; //用set是为了去重
        int hash[1010] = {0};
        for (int num : nums1) { //先记录num1出现的元素
            hash[num] = 1;
        }
        for (int num : nums2) {
            if (hash[num] == 1) { //如果num2的元素在1中存在,插入result中
                result.insert(num);
            }
        }
        return vector<int>(result.begin(), result.end());
    }
};
  • 时间复杂度: O(n + m) m 是最后要把 set转成vector
  • 空间复杂度: O(n)

Leetcode 202.快乐数

题目链接:Leetcode 202.快乐数

题目描述:编写一个算法来判断一个数 n 是不是快乐数。

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

如果 n 是快乐数就返回 True ;不是,则返回 False 。

思路:首先我们思考一下一个数经过转换会有几种可能性?

(1)最终会得到 1。

(2)最终会进入循环。

(3)值会越来越大,最后接近无穷大。

对于第三种情况,根据本题的数据范围可以简单证明是错误的。

该数字有10位数,假设n为10个9,sum为810。虽然本题取不到,不过任何一个小于10位数求出的sum都不会超过这个。因此我们可以知道,一个数字在不断求sum的过程中,整体趋势上是变小的,因此(3)的情况不存在。

接下来思考什么时候会进入循环?

既然刚才已经证明了(3)是错的,那无限循环只有一种可能性,经过转换后sum重复出现了。

兜兜转转还是回到了判断集合中是否含有重复元素这一问题上,因此可以采用哈希法。我们将每次转换后的sum放入哈希表内,每次转换之前判断哈希表内是否含有sum,如果有,返回false;否则一直找到1为止。

第一种方法:(哈希法)

class Solution {
public:
    int getSum(int n) { //获取sum
        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 (sum == 1)
                return true;
            if (set.find(sum) != set.end()) {
                return false;
            } else {
                set.insert(sum);
            }
            n = sum;
        }
    }
};

本题的时间复杂度证明过程我不太理解,因此贴上leetcode题解区的图片:

  • 时间复杂度: O(logn)
  • 空间复杂度: O(logn)

不知道大家还记不记得这道题的做法:142. 环形链表 II

我之前也写过这道题,用的双指针法来判断是否含有环,本题也可以用这个方法。

第二种方法:(双指针法)

(1)如果存在环,则快慢指针会相遇在环入口处

(2)如果不存在环,则快指针会变成1

class Solution {
public:
    int getSum(int n) { //获取sum
        int sum = 0;
        while (n) {
            sum += (n % 10) * (n % 10);
            n /= 10;
        }
        return sum;
    }
    bool isHappy(int n) {
        int slow = n;
        int fast = getSum(n);
        while (fast != 1 && slow != fast) {
            //慢指针一次走一步,快指针一次走两步
            slow = getSum(slow);
            fast = getSum(getSum(fast));
        }
        //如果不存在环,会因为fast==1跳出循环
        //如果存在环,则fast==slow!=1
        return fast == 1;
    }
};
  • 时间复杂度: O(logn)
  • 空间复杂度: O(1)

Leetcode 1. 两数之和

题目链接:Leetcode 1. 两数之和

题目描述:

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

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。

思路:最简单的还是两层for循环暴力枚举,就不过多赘述了。主要介绍哈希法:本题需要一个集合来存放我们遍历过的元素,然后在遍历数组的时候去询问这个集合,判断target-nums[i]是否出现在这个集合。由于本题不仅需要储存数组元素值,还要储存对应数组下标,因此使用map更好。

代码如下:

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> map;
        for (int i = 0; i < nums.size(); i++) {
            //遍历当前元素,查找map中是否有匹配的key
            auto it = map.find(target - nums[i]);
            if (it != map.end()) {
                return {it->second, i};
            }
            //如果没找到就把当前的元素值和下标放入map
            map.insert(pair<int, int>(nums[i], i));
        }
        return {};
    }
};
  • 时间复杂度: O(n)
  • 空间复杂度: O(n)

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

最后,如果文章有错误,请在评论区或私信指出,让我们共同进步!

  • 19
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值