代码随想录-Day-06

242. 有效的字母异位词

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

注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。
示例 1: 输入: s = “anagram”, t = “nagaram” 输出: true

示例 2: 输入: s = “rat”, t = “car” 输出: false

方法一:排序

class Solution {
    public boolean isAnagram(String s, String t) {
        if (s.length() != t.length()) {
            return false;
        }
        char[] str1 = s.toCharArray();
        char[] str2 = t.toCharArray();
        Arrays.sort(str1);
        Arrays.sort(str2);
        return Arrays.equals(str1, str2);
    }
}

这段代码定义了一个名为 Solution 的类,其中包含一个名为 isAnagram 的公共方法。这个方法用于判断两个字符串 st 是否为彼此的字母异位词(anagram)。字母异位词指的是组成两个单词的字符种类和每种字符的数量完全相同,但字符的顺序可以不同,例如 “listen” 和 “silent”。

方法的逻辑如下:

  1. 长度检查:首先,检查两个字符串的长度。如果长度不相等,它们不可能是异位词,因此直接返回 false

  2. 转换为字符数组:然后,将字符串 st 分别转换为字符数组 str1str2。这是为了能够对字符进行排序和比较。

  3. 排序:使用 Arrays.sort() 方法对两个字符数组进行排序。排序后,如果两个字符串是异位词,它们排序后的数组应该是完全相同的。

  4. 比较并返回结果:最后,使用 Arrays.equals() 方法比较排序后的两个字符数组是否相等。如果相等,说明 st 是异位词,返回 true;否则,返回 false

这种方法简单直观,但注意它在最坏情况下的时间复杂度为 O(n log n),其中 n 是字符串的长度,因为排序操作是最耗时的部分。如果面试官期望更高效的解法,可以考虑使用字符计数的方法,即将每个字符出现的次数进行统计然后比较,这样可以在 O(n) 时间内完成。

方法二:哈希表

class Solution {
    public boolean isAnagram(String s, String t) {
        if (s.length() != t.length()) {
            return false;
        }
        int[] table = new int[26];
        for (int i = 0; i < s.length(); i++) {
            table[s.charAt(i) - 'a']++;
        }
        for (int i = 0; i < t.length(); i++) {
            table[t.charAt(i) - 'a']--;
            if (table[t.charAt(i) - 'a'] < 0) {
                return false;
            }
        }
        return true;
    }
}

这段代码定义了一个名为 Solution 的类,其中包含一个公共方法 isAnagram,用于判断两个字符串 st 是否为字母异位词(anagrams)。字母异位词是指两个字符串中的字符种类和每种字符的数量完全相同,但顺序可以不同,例如 “listen” 和 “silent”。

方法的逻辑如下:

  1. 长度检查:首先,比较两个字符串的长度。如果长度不相等,它们不可能是异位词,直接返回 false

  2. 字符计数表:定义一个大小为26的小写英文字母表 table,索引是从 ‘a’ 开始的,因此 ‘a’ 对应索引0,‘b’ 对应索引1,依此类推直到 ‘z’ 对应索引25。这个表用于统计每个字符出现的次数。

  3. 统计s中的字符:遍历字符串 s,将每个字符出现的次数在 table 中相应位置累加。这里利用了字符 'a' 的ASCII值做偏移位,减去 'a' 的ASCII值,得到的差即为字符在表中的索引。

  4. 校验t中的字符:遍历字符串 t,减少 table 中对应字符的计数。如果在减少后某字符的计数变为负数,说明 t 中该字符的出现次数超过了 s 中的次数,直接返回 false

  5. 返回结果:如果遍历完 t 后没有返回 false,说明两字符串是异位词,返回 true

这种方法的时间复杂度为 O(n),其中 n 是字符串的长度,因为它只需要遍历每个字符串一次,相比之前的排序解法更高效。空间复杂度为 O(1),因为固定大小的计数表不依赖于输入字符串的长度。

349. 两个数组的交集

给定两个数组 nums1 和 nums2 ,返回 它们的 交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
在这里插入图片描述

方法:两个集合

class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        Set<Integer> set1 = new HashSet<Integer>();
        Set<Integer> set2 = new HashSet<Integer>();
        for (int num : nums1) {
            set1.add(num);
        }
        for (int num : nums2) {
            set2.add(num);
        }
        return getIntersection(set1, set2);
    }

    public int[] getIntersection(Set<Integer> set1, Set<Integer> set2) {
        if (set1.size() > set2.size()) {
            return getIntersection(set2, set1);
        }
        Set<Integer> intersectionSet = new HashSet<Integer>();
        for (int num : set1) {
            if (set2.contains(num)) {
                intersectionSet.add(num);
            }
        }
        int[] intersection = new int[intersectionSet.size()];
        int index = 0;
        for (int num : intersectionSet) {
            intersection[index++] = num;
        }
        return intersection;
    }
}

这段代码定义了一个名为 Solution 的类,其中包含两个方法,用来找出两个整数数组(nums1nums2)的交集,并以数组的形式返回结果。交集元素不重复,且结果数组中的元素按元素的升序排列(尽管代码逻辑中并未直接排序,但因HashSet的特性及遍历顺序,结果会自然按升序输出)。

方法分析:

1. intersection 方法
  • 输入:两个整数数组 nums1nums2
  • 输出:它们的交集数组。
  • 逻辑
    1. 分别将两个数组中的元素添加到两个哈希集合(HashSet)中,哈希集合自动去重。
    2. 调用 getIntersection 方法,传入两个集合,以获取交集。
2. getIntersection 方法
  • 输入:两个哈希集合 set1set2,代表两个数组去重后的元素集合。
  • 输出:两个集合的交集,以数组形式返回。
  • 逻辑
    1. 优化:如果 set1 的大小大于 set2,交换集合进行处理,以减少遍历次数,优化性能。
  1. 初始化一个新的哈希集合 intersectionSet 用于存放交集。
  2. 遍历较小集合 (set1),检查每个元素是否在另一个集合 (set2) 中存在,存在则添加到 intersectionSet
  3. intersectionSet 转换为数组:初始化一个大小等于交集大小的数组 intersection,遍历 intersectionSet,将元素填充到数组中。
  4. 返回交集数组。

总结

这段代码实现了一个高效寻找两个整数数组交集的方法,利用哈希集合自动去重和查找存在的特性,简化了查找交集的过程。通过先转换为集合,再求交集,最后转换回数组的形式,实现了既去重又找交集的目标,且代码逻辑清晰易懂。

202. 快乐数

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

「快乐数」 定义为:

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

输入:19
输出:true
解释:
1^2 + 9^2 = 82
8^2 + 2^2 = 68
6^2 + 8^2 = 100
1^2 + 0^2 + 0^2 = 1
在这里插入图片描述
在这里插入图片描述
根据我们的探索,我们猜测会有以下三种可能。

最终会得到 111。
最终会进入循环。
值会越来越大,最后接近无穷大。

方法:用哈希集合检测循环

class Solution {
    private int getNext(int n) {
        int totalSum = 0;
        while (n > 0) {
            int d = n % 10;
            n = n / 10;
            totalSum += d * d;
        }
        return totalSum;
    }

    public boolean isHappy(int n) {
        Set<Integer> seen = new HashSet<>();
        while (n != 1 && !seen.contains(n)) {
            seen.add(n);
            n = getNext(n);
        }
        return n == 1;
    }
}

这段代码定义了一个名为Solution的类,该类包含了两个方法:getNextisHappy。这段代码是用来实现一个经典的编程问题——快乐数(Happy Number)判断。快乐数是指,将这个数的各个位数的平方和相加,然后再将这个和的各位数平方和相加,如此反复进行下去,最终会得到1,则称这个数为快乐数;若无限循环不为1,则不是快乐数。

getNext方法

  • 功能:计算一个整数的各位数字的平方和。
  • 参数n —— 输入的整数。
  • 返回值:计算得到的各位数字平方和。
  • 逻辑
    1. 初始化totalSum为0,用于累加每位数字的平方和。
    2. n > 0时,循环执行以下操作:
      • n的个位数dn % 10)。
      • n除以10并整除,去掉个位数(n / 10)。
      • d的平方累加到totalSum中。
  • 返回累加后的totalSum

isHappy方法

  • 功能:判断一个整数是否为快乐数。
  • 参数n —— 需要判断的整数。
  • 返回值true如果n是快乐数,否则false
  • 逻辑
    1. 初始化一个哈希集合seen来存储已经计算过的数,避免无限循环。
    2. n不等于1且seen中没有见过n时,执行循环:
      • n添加到seen中,标记已访问。
      • 更新nn的下一个数字的平方和(调用getNext(n))。
    3. 循环结束时,如果n为1,说明是快乐数,返回true;否则,不是快乐数,返回false

整体来看,这段代码实现了判断一个整数是否为快乐数的逻辑,利用了循环和哈希集合来避免无限循环,以及计算每一位数字的平方和的逻辑来逐步检验是否能收敛到1。

1. 两数之和

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

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

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

示例 1:

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

方法一:暴力枚举

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int n = nums.length;
        for (int i = 0; i < n; ++i) {
            for (int j = i + 1; j < n; ++j) {
                if (nums[i] + nums[j] == target) {
                    return new int[]{i, j};
                }
            }
        }
        return new int[0];
    }
}

这段代码定义了一个名为 Solution 的类,其中包含一个方法 twoSum。这个方法用于解决经典的 “两数之和” 问题:给定一个整数数组 nums 和一个目标值 target,找出数组中和为目标值的那两个数的索引,并以数组形式返回这两个索引。注意,答案中的索引是无序的,且保证一定存在唯一解。

代码逻辑分析:

  1. 初始化:首先获取数组的长度 n

  2. 双重循环

    • 外层循环(由变量 i 控制)遍历数组中的每个元素。
    • 内层循环(由变量 j 控制)从 i+1 开始遍历到数组末尾,这样保证了不会重复计算且避免自我匹配(比如 (i, i) 的情况)。
    • 条件判断:在内层循环中,检查当前的 nums[i]nums[j] 之和是否等于目标值 target
      • 如果相等,立即返回一个新数组,包含两个索引 {i, j}
  3. 特殊情况处理:如果遍历完所有可能的组合都没有找到满足条件的索引对,理论上根据题目描述应该不存在这种情况(因为保证有唯一解),但为了代码完整性,这里返回一个空数组 {}

注意事项:

  • 代码中存在一个小错误,外层循环的条件应当是 i < n 而非 i++。修正后的循环应为 for (int i = 0; i < n; ++i) 改为 for (int i = 0; i < n; i++)。这是书写习惯问题,不影响逻辑理解,但可能导致语法错误。
  • 代码逻辑本身可以优化,例如使用哈希表来降低时间复杂度至O(n),但基于原始问题要求,此解答是直接的暴力解法实现。

方法二:哈希表

class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> hashtable = new HashMap<Integer, Integer>();
        for (int i = 0; i < nums.length; ++i) {
            if (hashtable.containsKey(target - nums[i])) {
                return new int[]{hashtable.get(target - nums[i]), i};
            }
            hashtable.put(nums[i], i);
        }
        return new int[0];
    }
}

这段代码定义了一个名为 Solution 的类,其中包含一个方法 twoSum,用于解决「两数之和」问题。给定一个整数数组 nums 和一个目标值 target,找到 nums 中的两个数,使得它们的和等于 target,返回这两个数的数组下标。

代码逻辑分析:

  1. 初始化哈希表(HashMap):创建一个哈希表 hashtable 来存储数组中的值及其对应的下标,以供快速查找。

  2. 遍历数组:遍历数组 nums

    • 对于每个元素 nums[i],计算 target - nums[i],查看该差值是否在 hashtable 中存在。
    • 若存在,说明找到了和为目标值的一对数,直接返回这对数在原数组中的下标 {hashtable.get(target - nums[i]), i}
    • 若不存在,将当前元素及其下标 nums[i], i 存入 hashtable,以便后续查找。
  3. 特殊情况处理:如果遍历完整个数组都没找到符合条件的数对(理论上题目保证有解,此处逻辑更多是出于代码完整性考虑),返回一个空数组 {}

代码优化点:

  • 相较于暴力解法(两重循环),该方法通过使用哈希表将时间复杂度优化到了 O(n),空间换时间的经典范例。
  • 注意,代码中存在一处小错误,外层循环的条件应为 i < nums.length 而非 i++,正确写法应为 for (int i = 0; i < nums.length; ++i),这是书写疏忽导致的笔误,需修正以避免语法错误。

整体而言,此段代码实现了一个高效且常见的「两数之和」问题解决方案,利用哈希表降低了搜索配对的成本,是面试和实际应用中的常见解法。

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值