在编程面试和算法学习中,我们经常会遇到各种有趣的问题。本文将通过五个经典的算法问题,带你深入理解不同的解题思路和技巧。每个问题都有其独特的挑战,我们将逐一解析并提供清晰的解决方案。
1. 糖果分配问题
问题描述
Alice有n枚糖,每枚糖有一个类型。医生建议她只吃掉n/2枚糖(n为偶数)。Alice希望在遵循医生建议的情况下,尽可能吃到最多不同种类的糖。
示例说明:
示例 1:
输入:candyType = [1,1,2,2,3,3]
输出:3
解释:Alice只能吃 6/2 = 3 枚糖,由于只有3种糖,她可以每种吃一枚。
示例 2:
输入:candyType = [1,1,2,3]
输出:2
解释:Alice只能吃 4/2 = 2 枚糖,不管她选择吃的种类是 [1,2]、[1,3] 还是 [2,3],她只能吃到两种不同类的糖。
示例 3:
输入:candyType = [6,6,6,6]
输出:1
解释:Alice只能吃 4/2 = 2 枚糖,尽管她能吃2枚,但只能吃到1种糖。
解题思路
- Alice最多能吃n/2枚糖
- 她能吃到的种类数不会超过总种类数
- 因此,答案是总种类数和n/2中的较小值
代码实现
class Solution {
public int distributeCandies(int[] candyType) {
Set<Integer> set = new HashSet<Integer>();
for (int candy : candyType) {
set.add(candy);
}
return Math.min(set.size(), candyType.length / 2);
}
}
复杂度分析
- 时间复杂度:O(n),需要遍历所有糖果
- 空间复杂度:O(n),最坏情况下需要存储所有类型的糖果
2. 只出现一次的数字 II
问题描述
输入:nums = [2,2,3,2]
输出:3
示例 2:
输入:nums = [0,1,0,1,0,1,99]
输出:99
解题思路
- 哈希表法:统计每个数字的出现频率
- 位运算法:利用数字的二进制表示,统计每位出现的次数
哈希表实现
class Solution {
public int singleNumber(int[] nums) {
Map<Integer, Integer> freq = new HashMap<Integer, Integer>();
for (int num : nums) {
freq.put(num, freq.getOrDefault(num, 0) + 1);
}
int ans = 0;
for (Map.Entry<Integer, Integer> entry : freq.entrySet()) {
int num = entry.getKey(), occ = entry.getValue();
if (occ == 1) {
ans = num;
break;
}
}
return ans;
}
}
复杂度分析
- 时间复杂度:O(n)
- 空间复杂度:O(n)
3. 将有序数组转换为二叉搜索树
问题描述

解题思路
- 对于每个节点,左子树的所有值小于节点值
- 右子树的所有值大于节点值
- 左右子树的高度差不超过1
- 选择中间元素作为根节点
- 递归构建左子树和右子树
代码实现
class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
return helper(nums, 0, nums.length - 1);
}
public TreeNode helper(int[] nums, int left, int right) {
if (left > right) {
return null;
}
// 总是选择中间位置左边的数字作为根节点
int mid = (left + right) / 2;
TreeNode root = new TreeNode(nums[mid]);
root.left = helper(nums, left, mid - 1);
root.right = helper(nums, mid + 1, right);
return root;
}
}
复杂度分析
- 时间复杂度:O(n),每个元素只访问一次
- 空间复杂度:O(log n),递归栈的深度
4. 跳跃游戏
问题描述
输入:nums = [2,3,1,1,4]输出:true解释:可以先跳1步,从下标0到达下标1,然后再从下标1跳3步到达最后一个下标。
示例 2:
输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为3的位置。但该下标的最大跳跃长度是0,所以永远不可能到达最后一个下标。
解题思路
- 维护一个变量rightmost,表示当前能到达的最远位置
- 遍历数组,更新rightmost
- 如果rightmost能覆盖最后一个位置,返回true
代码实现
public class Solution {
public boolean canJump(int[] nums) {
int n = nums.length;
int rightmost = 0;
for (int i = 0; i < n; i++) {
if (i <= rightmost) {
rightmost = Math.max(rightmost, i + nums[i]);
if (rightmost >= n - 1) {
return true;
}
}
}
return false;
}
}
复杂度分析
- 时间复杂度:O(n)
- 空间复杂度:O(1)
5. 第三大的数
问题描述
输入:[3, 2, 1]
输出:1
解释:第三大的数是1。
示例 2:
输入:[1, 2]
输出:2
解释:第三大的数不存在,所以返回最大的数2。
示例 3:
输入:[2, 2, 3, 1]
输出:1
解释:注意,要求返回第三大的数,是指在所有不同数字中排第三大的数。
此例中存在两个值为2的数,它们都排第二。在所有不同数字中排第三大的数为1。
解题思路
- 遍历数组,维护三个变量a、b、c,分别表示第一、第二和第三大的数
- 注意处理重复值和null值的情况
代码实现
class Solution {
public int thirdMax(int[] nums) {
Integer a = null, b = null, c = null;
for (int num : nums) {
if (a == null || num > a) {
c = b;
b = a;
a = num;
} else if (a > num && (b == null || num > b)) {
c = b;
b = num;
} else if (b != null && b > num && (c == null || num > c)) {
c = num;
}
}
return c == null ? a : c;
}
}
复杂度分析
- 时间复杂度:O(n)
- 空间复杂度:O(1)
总结
- 集合应用:糖果分配问题展示了如何利用集合特性快速解决问题,示例清晰地说明了不同情况下的输出结果
- 频率统计:只出现一次的数字问题展示了哈希表在频率统计中的应用,示例帮助我们理解"出现三次"的具体含义
- 分治策略:平衡二叉搜索树问题展示了分治思想在树结构中的应用,多个示例输出说明了平衡BST的不唯一性
- 贪心算法:跳跃游戏问题展示了贪心思想在路径寻找中的应用,示例生动地展示了能否到达终点的两种情况
- 变量维护:第三大的数问题展示了如何通过有限变量维护状态信息,示例特别强调了重复值的处理
14

被折叠的 条评论
为什么被折叠?



