引言:数组问题的艺术
数组是算法竞赛和面试中最基础也是最常见的数据结构。掌握数组问题的解决技巧,不仅能够帮助我们在面试中脱颖而出,更能培养我们的计算思维和问题分析能力。本文将深入解析五道经典的数组问题,从简单到中等难度,涵盖统计、搜索、排序、位运算和贪心算法等多个重要领域。
1. 去掉最低工资和最高工资后的工资平均值
问题描述
给定一个唯一整数数组表示员工工资,去掉最低工资和最高工资后,计算剩余工资的平均值。
示例分析:
输入:salary = [4000, 3000, 1000, 2000]
输出:2500.00000
解释:去掉1000(最低)和4000(最高)后,(2000+3000)/2 = 2500
解法一:单次遍历法
class Solution {
public double average(int[] salary) {
double sum = 0;
double maxValue = Integer.MIN_VALUE;
double minValue = Integer.MAX_VALUE;
for (int num : salary) {
sum += num;
maxValue = Math.max(maxValue, num);
minValue = Math.min(minValue, num);
}
return (sum - maxValue - minValue) / (salary.length - 2);
}
}
解法二:排序法
class Solution {
public double average(int[] salary) {
Arrays.sort(salary);
double sum = 0;
for (int i = 1; i < salary.length - 1; i++) {
sum += salary[i];
}
return sum / (salary.length - 2);
}
}
算法对比
|
方法 |
时间复杂度 |
空间复杂度 |
适用场景 |
|
单次遍历 |
O(n) |
O(1) |
推荐 ,效率最高 |
|
排序法 |
O(n log n) |
O(1) |
代码简单,小数据量 |
思维拓展
这个问题教会我们如何在单次遍历中同时维护多个统计量,这是处理实时数据流的常用技巧。
2. 两个数组的最小公共整数
问题描述
给定两个非降序数组,找到它们的最小公共整数。如果没有公共整数,返回-1。
示例分析:
输入:nums1 = [1,2,3], nums2 = [2,4]
输出:2
解释:公共整数是2
解法一:双指针法
class Solution {
public int getCommon(int[] nums1, int[] nums2) {
int i = 0, j = 0;
while (i < nums1.length && j < nums2.length) {
if (nums1[i] == nums2[j]) {
return nums1[i];
} else if (nums1[i] < nums2[j]) {
i++;
} else {
j++;
}
}
return -1;
}
}
解法二:二分搜索法
class Solution {
public int getCommon(int[] nums1, int[] nums2) {
// 对较短的数组进行遍历,在较长的数组中二分搜索
for (int num : nums1) {
if (binarySearch(nums2, num)) {
return num;
}
}
return -1;
}
private boolean binarySearch(int[] arr, int target) {
int left = 0, right = arr.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (arr[mid] == target) {
return true;
} else if (arr[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return false;
}
}
算法对比
|
方法 |
时间复杂度 |
空间复杂度 |
适用场景 |
|
双指针 |
O(m+n) |
O(1) |
推荐 ,利用有序特性 |
|
二分搜索 |
O(m log n) |
O(1) |
一个数组远大于另一个 |
思维拓展
这个问题展示了如何利用"有序"这一特性来优化算法,双指针技巧在合并有序数组、寻找交集等问题中非常有用。
3. 判断能否形成等差数列
问题描述
判断数组能否重新排列形成等差数列。
示例分析:
输入:arr = [3,5,1]
输出:true
解释:可重排为[1,3,5],差值为2
解法:排序验证法
class Solution {
public boolean canMakeArithmeticProgression(int[] arr) {
if (arr.length < 2) return true;
Arrays.sort(arr);
int diff = arr[1] - arr[0];
for (int i = 2; i < arr.length; i++) {
if (arr[i] - arr[i - 1] != diff) {
return false;
}
}
return true;
}
}
边界情况处理
class Solution {
public boolean canMakeArithmeticProgression(int[] arr) {
if (arr.length <= 2) return true;
Arrays.sort(arr);
int expectedDiff = arr[1] - arr[0];
for (int i = 1; i < arr.length - 1; i++) {
int actualDiff = arr[i + 1] - arr[i];
if (actualDiff != expectedDiff) {
return false;
}
}
return true;
}
}
思维拓展
4. 根据前缀异或构造数组
问题描述
- 交换律:a ^ b = b ^ a
- 结合律:(a ^ b) ^ c = a ^ (b ^ c)
- 自反性:a ^ b ^ b = a
输入:pref = [5,2,0,3,1]输出:[5,7,2,3,2]解释:pref[0] = 5 = arr[0]pref[1] = 2 = 5 ^ arr[1] ⇒ arr[1] = 5 ^ 2 = 7
解法:异或性质应用
class Solution {
public int[] findArray(int[] pref) {
int[] res = new int[pref.length];
res[0] = pref[0];
for (int i = 1; i < pref.length; i++) {
res[i] = pref[i - 1] ^ pref[i];
}
return res;
}
}
数学推导
已知:pref[i] = arr[0] ^ arr[1] ^ ... ^ arr[i]
可得:pref[i-1] = arr[0] ^ arr[1] ^ ... ^ arr[i-1]
因此:arr[i] = pref[i-1] ^ pref[i]
因为:pref[i-1] ^ pref[i] = pref[i-1] ^ (pref[i-1] ^ arr[i]) = arr[i]
思维拓展
这个问题展示了位运算在算法中的巧妙应用。理解异或运算的性质对于解决这类问题至关重要。
5. 最大硬币数目
问题描述
3n堆硬币,每轮选3堆,你总是拿第二多的,求能拿到的最大硬币数。
示例分析:
输入:piles = [2,4,1,2,7,8]
输出:9
策略:配对(2,7,8)和(1,2,4),得到7+2=9
解法:贪心算法
class Solution {
public int maxCoins(int[] piles) {
Arrays.sort(piles);
int n = piles.length;
int rounds = n / 3;
int result = 0;
// 每次取倒数第二大的数
for (int i = n - 2; i >= rounds; i -= 2) {
result += piles[i];
}
return result;
}
}
策略分析
public class CoinStrategy {
public void explainStrategy(int[] piles) {
Arrays.sort(piles);
// 排序后: [1, 2, 2, 4, 7, 8]
// 最优策略:
// 让Alice拿最大的(8),Bob拿最小的(1),我拿第二大的(7)
// 然后Alice拿次大的(4),Bob拿次小的(2),我拿第三大的(2)
// 总计:7 + 2 = 9
}
}
算法证明
- 当前最大的(给Alice)
- 当前次大的(我们自己拿)
- 当前最小的(给Bob)
思维拓展
总结:算法思维模式
1. 统计思维
- 问题1:如何在单次遍历中维护多个统计量
- 关键技巧:同时跟踪最大值、最小值和总和
2. 双指针技巧
- 问题2:利用有序特性优化搜索
- 应用场景:有序数组的合并、交集、子数组问题
3. 排序预处理
- 问题3:通过排序简化问题
- 适用情况:当顺序不重要,但相对关系重要时
4. 位运算魔法
- 问题4:利用异或性质进行高效计算
- 重要性质:自反性、交换律、结合律
5. 贪心策略
- 问题5:通过局部最优达到全局最优
- 证明方法:数学归纳法或反证法
实战建议
- 理解问题本质:每个问题都有其核心的数学模型
- 利用数据特性:有序、唯一等特性可以大大优化算法
- 边界情况考虑:空数组、单元素、极值等情况
- 复杂度分析:根据数据规模选择合适的算法
1067

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



