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

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

今日学习的文章链接和视频链接

参考代码随想录

自己看到题目的第一想法

今天的题目之前都刷过,结合前几天经验来看,可能会有记不得,希望可以不看笔记写出来

自己实现过程中遇到哪些困难

  • 242.有效的字母异位词秒了
  • 349.两个数组的交集也秒了,学到了数组,set,和map做哈希表的特点和优缺点,特别学习了Python中set的数据结构,Java中hashtablehashset,和hashmap的对比
  • 202.快乐数看了笔记才写出来,特别学到用Python实现辅助方法getSum()中,遍历的条件为n = n // 10,而不是n /= 10,因为前者规定了向下取整,最终n会等于0
  • 1.两数之和也看了笔记,学到了Java中hashmap的几个常用方法,包括构造函数,map.containsKey()map.get(Key),和map.put(Key, Value)

今日收获,记录一下自己的学习时长

  • 应打卡7月3日,7月4日补打卡,学习时长4hr
  • 字符串*1,28. 找出字符串中第一个匹配项的下标,学习KMP算法
  • 进入栈和队列,希望可以加快进度,尽早进入树

0242. 有效的字母异位词 Valid Anagram

Leetcode 题目链接

1. 题目描述

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

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

示例1:

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

示例2:

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

2. 解题思路

  • 可以用暴力循环解法,时间复杂度为O(n^2)
  • 使用哈希表进行优化。数组就是一个哈希表,定义一个数组记录字符串s里字符出现的次数。

具体算法:

  • 定义一个大小为 26 的数组record,因为从小写字母az一共有26个字符
  • 把字符映射到哈希表的索引下标上,因为字符a到字符z的ASCII是26个连续的数值,所以字符a映射为下标0,相应的字符z映射为下标25。
  • 先遍历字符串s;遍历时,将s[i] - 'a'所在的元素做+1操作,不需要记住a的ASCII,只要求出一个相对值就可以
  • 再遍历字符串t;同样,在遍历时将t[i] - 'a'所在的元素做-1操作
  • 最后遍历哈希表record,如果有元素不为0,说明字符串s和字符串t所包含的字符不一样,他们不是字母异位词,返回False;如果所有record的元素都为0,返回True
  • 时间复杂度O(m + n)。其中字符串s长度为m,字符串呢t长度为n
  • 空间复杂度O(1)。因为只定义了一个常量大小的辅助数组。

3. 算法实现

3.1. Python

  • Python中字符串函数ord()把字符转化为相对应的int
class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        # 定义哈希表
        record = [0] * 26

        # 遍历s,记录对应字符出现次数+1
        # key是i-'a'
        for i in s:
            record[ord(i) - ord('a')] += 1
        
        # 遍历t,相对应的字符次数-1
        for j in t:
            record[ord(j) - ord('a')] -= 1

        # 遍历record,检查是否都为0
        for i in range(len(record)):
            if record[i] != 0:
                return False
        
        return True

3.2. Java

class Solution {
    public boolean isAnagram(String s, String t) {
        // hash table
        int[] record = new int[26];

        for (int i = 0; i < s.length(); i++) {
            record[s.charAt(i) - 'a'] ++;
        }

        for (int j = 0; j < t.length(); j++) {
            record[t.charAt(j) - 'a'] --;
        }

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

        return true;
    }
}

0349. 两个数组的交集 Intersection of Two Arrays

Leetcode 题目链接

1. 题目描述

给定两个数组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] 也是可通过的

2. 解题思路

  • 本题可以使用暴力枚举法,时间复杂度为O(n^2)
  • 使用哈希法进行优化
  • 注意题目要求输出结果是唯一的,说明需要对输出结果进行去重,同时可以不考虑输出结果的顺序

2.1 哈希表的选择

  • 使用数组做哈希表的前提条件是明确知道哈希表的大小,本题中原始题目没有限制数组的范围,所以不可以用数组做哈希表;后续题目改进,限制了0 <= nums1[i], nums2[i] <= 1000,才可以用数组做哈希表
  • 本题中学习使用set做哈希表,set是一个无序的没有重复的集合,符合题目要求的输出结果唯一。
  • set和数组相比,占用空间更大,速度更慢。

2.2 具体算法

  • 定义一个集合record用来存储两个数组交集的元素
  • 遍历数组nums1,定义第一个集合set1作为哈希表存储出现过的次数
  • 再遍历数组nums2,如果出现在set1中,就把出现过的元素存储在record
  • 最后把集合record转换为list
  • 时间复杂度O(m + n)。其中字符串num1长度为m,字符串呢nums2长度为n
  • 空间复杂度O(1)。因为只定义了一个辅助集合。

3. 算法实现

3.1 Python - Intersection()

  • 直接使用库函数set.intersection(),返回的set转为列表
class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        return list(set(nums1).intersection(set(nums2)))

3.2 Python - set做哈希表

class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        record = set()
        set1 = set()

        # 遍历nums1
        for num in nums1:
            set1.add(num)

        # 遍历nums2
        for num in nums2:
            if num in set1:
                record.add(num)

        # set -> list
        return list(record)

3.3 Python - 数组做哈希表

class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        record = set()
        
        # 数组做哈希表
        hash = [0] * 1001
        
        # 出现在nums1的元素在hash中做记录
        for num in nums1:
            hash[num] = 1

        # nums2中出现的元素,如果nums1也出现过,record记录
        for num in nums2:
            if hash[num] == 1:
                record.add(num)

        # set -> list
        return list(record)

3.3 Java - hashset 做哈希表

class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        HashSet<Integer> record = new HashSet<>();

        // hashset做哈希表
        HashSet<Integer> set1 = new HashSet<>();

        // 遍历nums1
        for (int num1: nums1) {
            set1.add(num1);
        }

         // 遍历nums2
         for (int num2: nums2) {
             if (set1.contains(num2)) {
                 record.add(num2);
             }
         }

        // set -> list
        int len = record.size();
        int[] res = new int[len];
        int i = 0;
        for (int num: record) {
            res[i] = num;
            i++;
        }

        return res;
    }
}

0202. 快乐数 Happy Number

Leetcode 题目链接

1. 题目描述

编写一个算法来判断一个数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

2. 解题思路

  • 当要快速判断一个元素是否在集合里时,考虑使用 哈希表
  • 题目说明,在求和的过程中可能会无限循环,即求得的和sum可能会重复出现,所以使用 哈希表 判断;如果sum重复出现,就return false,否则一直找到sum == 1为止。
  • 求和的过程需要用到对数值各个位上的单数操作。
  • 时间复杂度O(log(n))
  • 空间复杂度O(log(n))

具体算法:

  • 定义辅助方法get_sum计算每个位数平方的和
  • 定义一个集合record用来存储每次计算出来的和sum,并且判断如果本次计算的和sum已经出现在record中,说明出现了无限循环,返回false;如果sum == 1,返回true

3. 算法实现

3.1 Python - getSum()辅助方法

  • 注意在辅助方法getSum()中,遍历的条件为n = n // 10,而不是n /= 10,因为前者规定了向下取整,最终n会等于0
class Solution:
    def isHappy(self, n: int) -> bool:
        # set做哈希表
        hash = set()

        while True:
            n = self.getSum(n)

            if n == 1:
                return True
            elif n in hash: # 判断是否有无限循环
                return False

            hash.add(n)

    def getSum(self, n: int) -> int:
        sum = 0

        while n != 0:
            dig = n % 10
            sum += dig ** 2
            n = n // 10

        return sum

3.2 Java - getSum()辅助方法

class Solution {
    public boolean isHappy(int n) {
        Set<Integer> record = new HashSet<>();
        while (n != 1) {
            if (record.contains(n)) {
                return false;
            } else {
                record.add(n);
                n = this.getSum(n);
            }
        }

        return true;
    }

    private int getSum(int n) {
        int sum = 0;

        while (n > 0) {
            int digit = n % 10;
            sum += digit * digit;
            n = n / 10;
        }

        return sum;
    }
}

3.3 Python - 使用集合 + 精简

class Solution:
    def isHappy(self, n: int) -> bool:
        record = set()

        while n != 1:
            n = sum(int(i) ** 2 for i in str(n))
            if n in record:
                return False
            else:
                record.add(n)
        return True

3.4 Python - 使用数组

class Solution:
    def isHappy(self, n: int) -> bool:
        record = []

        while n != 1:
            n = sum(int(i) ** 2 for i in str(n))
            if n in record:
                return False
            else:
                record.append(n)
        return True

0001. 两数之和 Two Sum

Leetcode 题目链接

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]

2. 解题思路

2.1 什么时候使用哈希法

  • 当查询一个元素是否出现过,或者一个元素是否出现在集合里面时,使用哈希法
  • 本题中,遍历数组num时,除了知道每个元素的值,还要知道所对应的数组下标,适合用key-value结构来存放,key用来存放数组元素,value存放数组元素的下标

2.2 使用数组array和集合set做哈希表的局限

  • 使用数组array做哈希表时,数组大小受限;而且如果元素很少,而哈希值太大,会造成 空间浪费
  • 使用集合set时,里面只能有一个key。本题需要返回xy的下标,所以不能用set

2.3 选择map做哈希表

  • map是一种 key-value 的数据结构
  • 使用map用来存储访问过的元素,用来在遍历数组时,记录之前遍历的哪些元素和相对应的下标,才能找到与当前元素匹配的元素(即两个元素相加等于target
  • map的存储结构为:{key: 数据元素, value: 元素所对应的下标}
  • 在遍历数组时,map中查询所需要的和当前元素相匹配的元素,如果有,返回两个匹配元素的下标对;如果没有,把当前元素加入map中。
    • 时间复杂度O(n)
  • 空间复杂度O(n)

2.4 具体算法

  • 定义哈希表record用来存储数组nums中的元素和所对应的下标;哈希表中key为元素的值,value为元素的下标
  • 遍历数组nums,计算该元素nums[i]target之间的差值,记为diff
  • 查询哈希表,如果表中有key等于差值diff的元素,那该元素符合条件,读取该元素的下标,即key == diff元素所对应的value;如果表中没有查询到,则表中插入该元素nums[i]的数值和下标
  • 返回两个符合条件元素的下标

3. 算法实现

3.1 Python - 暴力法

  • 时间复杂度为O(n^2)
class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        for i in range(len(nums)):
            for j in range(i + 1, len(nums)):
                if nums[i] + nums[j] == target:
                    return [i, j]

3.2 Python - 使用字典

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        # 字典做哈希表
        # key:num,value:下标
        record = dict()
        res = list()

        for i in range(len(nums)):
            diff = target - nums[i]

            # 判断diff是否出现在哈希表中
            if diff in record:
                res.append(record[diff])
                res.append(i)
            else:
                record[nums[i]] = i

        return res 

3.3 Java - 使用Map

  • Java中HashMap常用方法:
    • 构造函数:Map<Key, Velue> map = new HashMap<>();
    • 是否包含keymap.containsKey(Key)
    • 根据key读取对应的valuemap.get(Key)
    • 插入新的key-value值:map.put(Key, Value)
class Solution {
    public int[] twoSum(int[] nums, int target) {
        int[] res = new int[2]; // 返回res

        // 用map做哈希表
        Map<Integer, Integer> map = new HashMap<>();

        for (int i = 0; i < nums.length; i++) {
            int diff = target - nums[i];

            // 寻找对应的元素
            if (map.containsKey(diff)) {
                res[0] = i;
                res[1] = map.get(diff);
                break;
            } else {
                map.put(nums[i], i); // key - nums[i], value - i
            }
        }

        return res;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14训练营中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练营还提供了每日的讨论知识点,例如在第15的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值