每日算法-250515

每日算法打卡 - 2025-05-15

今天我完成了以下三道 LeetCode 算法题,特此记录解题思路与代码。


2342. 数位和相等数对的最大和

题目描述

给你一个下标从 0 开始的数组 nums ,数组中的元素都是 整数。请你选出两个下标 iji != j),且 nums[i] 的数位和 与 nums[j] 的数位和相等。
请你找出所有满足条件的下标对 (i, j) 中,nums[i] + nums[j]最大值 ,如果不存在满足条件的数对,请返回 -1

思路与解题过程

核心思路:哈希表(数组模拟)

题目要求找到数位和相等的两个不同数字,并使它们的和最大。

  1. 数位和计算:首先,我们需要一个辅助函数 getDigit(int x) 来计算一个整数 x 的所有数位之和。例如,getDigit(123) 返回 1+2+3=6
  2. 存储与查找
    • 由于 1 <= nums[i] <= 10^9,一个数的最大数位和是 999,999,999 的数位和,即 9 * 9 = 81。因此,我们可以使用一个大小为 82 (索引 0 到 81) 的数组 map 来记录每个数位和对应的 当前遇到的最大数字map[sum] 存储数位和为 sum 的数字中,目前遍历到的最大的那个。
    • 遍历 nums 数组中的每一个数 x
      • 计算 x 的数位和 digitSum = getDigit(x)
      • 查看 map[digitSum] 中是否已经存储了值 (设为 oldVal)。
        • 如果 oldVal != 0 (或者一个特定的初始标记值,这里用 0 表示未存储,因为题目中 nums 元素都是正整数),说明之前已经遇到过一个数位和也为 digitSum 的数字 oldVal。那么 xoldVal 就形成了一个数位和相等的数对。它们的和是 x + oldVal。我们用这个和去更新全局的最大和 ret
        • 无论 oldVal 是否为 0,我们都需要更新 map[digitSum]。为了确保将来能组成更大的和,map[digitSum] 应该存储数位和为 digitSum 的数中,目前遇到的那个最大的数。所以,map[digitSum] = Math.max(map[digitSum], x)
  3. 初始化ret 初始化为 -1map 数组初始化为 0

复杂度分析

  • 时间复杂度: O ( N ⋅ log ⁡ M ) O(N \cdot \log M) O(NlogM),其中 N N Nnums 数组的长度, M M Mnums 中元素的最大值。计算每个数字的数位和需要 O ( log ⁡ M ) O(\log M) O(logM) 的时间(因为数字的位数与 log ⁡ M \log M logM 成正比)。
  • 空间复杂度: O ( C ) O(C) O(C),其中 C C C 是最大可能的数位和。在这里 C = 81 C=81 C=81,所以是 O ( 1 ) O(1) O(1) 的常数空间。

代码实现

class Solution {
    public int maximumSum(int[] nums) {
        int ret = -1;
        int[] map = new int[82]; 
        
        for (int x : nums) {
            int digitSum = getDigitSum(x);
            int oldVal = map[digitSum];
            
            if (oldVal != 0) { 
                ret = Math.max(ret, (oldVal + x));
            }
            map[digitSum] = Math.max(oldVal, x); 
        }
        return ret;
    }

    private int getDigitSum(int x) {
        int sum = 0;
        while (x > 0) {
            sum += x % 10;
            x /= 10;
        }
        return sum;
    }
}

121. 买卖股票的最佳时机

题目描述

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0

思路与解题过程

核心思路:一次遍历,记录最低价格和最大利润

我们只需要遍历一次价格数组,在遍历过程中动态维护两个关键信息:

  1. minPrice:到目前为止遇到的最低股票价格。
  2. maxProfit:到目前为止可以实现的最大利润。

遍历 prices 数组中的每个价格 currentPrice

  • 计算潜在利润:如果 currentPrice > minPrice,说明如果在 minPrice 时买入,在 currentPrice 时卖出,是可以盈利的。其利润为 currentPrice - minPrice。我们将这个潜在利润与 maxProfit 比较,更新 maxProfit = Math.max(maxProfit, currentPrice - minPrice)
  • 更新最低价格:无论当前是否能盈利,我们都需要用 currentPrice 来更新 minPrice,即 minPrice = Math.min(minPrice, currentPrice)。这样做是为了确保 minPrice 始终是历史最低点,为后续计算潜在利润做准备。

初始时,maxProfit0minPrice 可以设置为 prices[0] 或一个极大值。

复杂度分析

  • 时间复杂度: O ( N ) O(N) O(N), 其中 N N Nprices 数组的长度。我们只需要遍历数组一次。
  • 空间复杂度: O ( 1 ) O(1) O(1), 我们只使用了几个额外的变量。

代码实现

class Solution {
    public int maxProfit(int[] prices) {
        int maxProfit = 0;
        int minPrice = prices[0]; 
        
        for (int i = 0; i < prices.length; i++) {
            int currentPrice = prices[i];
            if (minPrice < currentPrice) {
                maxProfit = Math.max(maxProfit, (currentPrice - minPrice));
            }
            minPrice = Math.min(minPrice, currentPrice);
        }
        return maxProfit;
    }
}

76. 最小覆盖子串(复习)

题目描述

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 ""

注意:

  • 对于 t 中重复字符,我们寻找的子串中该字符数量必须不少于 t 中该字符数量。
  • 如果 s 中存在这样的子串,我们保证它是唯一的答案。

复习心得

这是第二次做这道题,对滑动窗口的理解更深了。核心在于如何判断当前窗口是否“合法”(即覆盖了 t 中所有字符及对应数量),以及在合法的基础上如何收缩窗口以找到最小长度。

详细题解之前写过,可参考:每日算法-250331

代码实现

class Solution {
    public String minWindow(String ss, String tt) {
        char[] s = ss.toCharArray();
        char[] t = tt.toCharArray();
        int retLen = s.length + 1;
        int[] map = new int[128];
        int kinds = 0;
        for (char c : t) {
            if (map[c] == 0) {
                kinds++;
            }
            map[c]++;
        }
        int kindAndCount = 0;
        int[] sMap = new int[128];
        int index = 0;
        for (int left = 0, right = 0; right < s.length; right++) {
            char in = s[right];
            if (++sMap[in] == map[in]) {
                kindAndCount++;
            }
            while (kindAndCount >= kinds) {
                if (retLen >= (right - left + 1)) {
                    retLen = (right - left + 1);
                    index = left;
                }
                char out = s[left];
                if (map[out] == sMap[out]--) {
                    kindAndCount--;
                }
                left++;
            }
        }
        return retLen == (s.length + 1) ? "" : ss.substring(index, index + retLen);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值