每日算法-250526
每日算法学习记录 - 25.05.26
今天记录两道完成的算法题,分享一下解题思路和代码。
面试题 16.24. 数对和
题目描述:

设计一个算法,找出数组中两数之和为指定值的所有整数对。一个数只能属于一个数对。
💡 思路
排序 + 双指针
⚙️ 解题过程
这个问题要求我们找出数组中所有和为 target 的数对,并且每个数字只能使用一次。
- 排序:首先对数组进行排序。这样做的目的是便于我们使用双指针法,从两端向中间查找。
- 双指针:
- 初始化左指针
left指向数组的开头(索引0)。 - 初始化右指针
right指向数组的末尾(索引nums.length - 1)。
- 初始化左指针
- 遍历和判断:
- 当
left < right时,持续迭代:- 计算当前左右指针指向的两个数
num1 = nums[left]和num2 = nums[right]的和currentSum = num1 + num2。 - 如果
currentSum == target:找到了一个数对。将(num1, num2)加入结果列表。因为每个数只能用一次,所以两个指针都需要移动,left++并且right--,以寻找下一对可能的数对。 - 如果
currentSum < target:说明当前和太小了,需要增大和。由于数组已排序,左边的数较小,右边的数较大,所以应将左指针left++向右移动,尝试一个更大的num1。 - 如果
currentSum > target:说明当前和太大了,需要减小和。应将右指针right--向左移动,尝试一个更小的num2。
- 计算当前左右指针指向的两个数
- 当
- 返回结果:当
left >= right时,遍历结束,返回收集到的所有数对。
📈 复杂度
- 时间复杂度: O ( N log N ) O(N \log N) O(NlogN)
Arrays.sort(nums)需要 O ( N log N ) O(N \log N) O(NlogN) 时间。- 双指针遍历本身需要 O ( N ) O(N) O(N) 时间。
- 因此,总时间复杂度由排序决定。
- 空间复杂度: O ( log N ) O(\log N) O(logN) 或 O ( N ) O(N) O(N) (取决于排序算法的实现)
💻 Code
class Solution {public List<List<Integer>> pairSums(int[] nums, int target) {// 1. 排序数组Arrays.sort(nums);List<List<Integer>> resultPairs = new ArrayList<>();int left = 0;int right = nums.length - 1;// 2. 双指针遍历while (left < right) {int num1 = nums[left];int num2 = nums[right];int currentSum = num1 + num2;if (currentSum == target) {// 找到了一个数对resultPairs.add(Arrays.asList(num1, num2));// 移动双指针,继续寻找其他数对left++;right--;} else if (currentSum < target) {// 和太小,移动左指针left++;} else {// 和太大,移动右指针right--;}}return resultPairs;}
}
3371. 识别数组中的最大异常值
题目描述:

给你一个整数数组 nums。数组中唯一一个不是众数的数字被称为「异常值」。数组的其余所有数字都是相等的「特殊数字」。请你返回「异常值」。
💡 思路
哈希表计数 + 数学推导/枚举
⚙️ 解题过程
题目指出,数组中有一个 “异常值”,其余所有数字都是相等的 “特殊数字”。设特殊数字的值为 s,异常值为 o。如果数组长度为 N,那么有 N-1 个 s 和 1 个 o。
数组总和 totalSum = (N-1) * s + o。
- 预处理:
- 计算数组
nums中所有元素的总和sum。 - 使用哈希表
map存储每个数字及其出现的次数。
- 计算数组
- 枚举与验证:
- 初始化一个结果变量
ret为一个足够小的值。 - 遍历数组中的每一个数
candidateS。我们假设这个candidateS就是那个 “特殊数字”s。 - 根据公式
2s + o = sum(其中s是candidateS,o是exceptionVal),可以推导出潜在的异常值exceptionVal = sum - 2 * candidateS。
- 初始化一个结果变量
- 判断与更新:
- 检查计算出的
exceptionVal是否存在于数组中。 - 如果
exceptionVal存在:- 情况一:
exceptionVal == candidateS- 这意味着推断出的异常值和我们假设的特殊数字是同一个值。
- 此时,要满足 "两个特殊数字
candidateS和一个异常值exceptionVal,这确保了至少有两个这样的数字。map.get(candidateS) > 1是一个必要的条件,确保我们能取出candidateS作为特殊值,同时exceptionVal(也等于candidateS) 也能在数组中找到。 - 如果条件满足,更新
ret = Math.max(ret, exceptionVal)。
- 情况二:
exceptionVal != candidateS- 这意味着异常值和特殊数字是不同的。
- 我们假设的
candidateS是特殊数字,exceptionVal是异常值。 - 我们只需要
exceptionVal存在于数组中即可(已通过map.containsKey(exceptionVal)检查)。同时,candidateS作为特殊数字也必须存在 (我们是从nums中选取的,所以它肯定存在)。 - 如果条件满足,更新
ret = Math.max(ret, exceptionVal)。
- 情况一:
- 检查计算出的
- 返回结果:遍历结束后,
ret中存储的就是找到的最大的符合条件的异常值。
📈 复杂度
- 时间复杂度: O ( N ) O(N) O(N)
- 计算总和和构建哈希表需要 O ( N ) O(N) O(N) 时间。
- 遍历数组(或哈希表的键集,最坏情况 N N N 个不同元素)进行检查,每次哈希表操作为 O ( 1 ) O(1) O(1) 平均时间。总共是 O ( N ) O(N) O(N)。
- 空间复杂度: O ( N ) O(N) O(N)
- 哈希表在最坏情况下可能需要存储 N N N 个不同的数。
💻 Code
class Solution {public int getLargestOutlier(int[] nums) {int largestOutlier = -2001; Map<Integer, Integer> counts = new HashMap<>();long totalSum = 0; for (int x : nums) {totalSum += x;counts.put(x, counts.getOrDefault(x, 0) + 1);}for (int specialNumCandidate : nums) { int potentialOutlier = (int) (totalSum - 2 * (long)specialNumCandidate);if (counts.containsKey(potentialOutlier)) {if (potentialOutlier == specialNumCandidate) {if (counts.get(potentialOutlier) > 1) {largestOutlier = Math.max(largestOutlier, potentialOutlier);}} else {largestOutlier = Math.max(largestOutlier, potentialOutlier);}}}return largestOutlier;}
}
