在面对 “游戏排名第三大的分数” 这一问题时,我们的任务是根据给定的分数数组,按照特定规则为小 M 确定一个合适的目标分数。
首先,明确我们要做的是从给定的分数数组中找到符合条件的目标分数。规则是如果分数数组中有三个或以上不同的分数,那就返回其中第三大的分数;要是不同的分数只有两个或更少,就选择最大的分数作为目标。为了能方便地找到第三大的分数或者确定最大的分数,一个很自然的想法就是先对分数数组进行排序,排序后,数组中的元素会按照从小到大的顺序排列,这样我们就可以从数组的末尾开始往前找,因为数组末尾的元素是最大的,越往前分数越小。
当数组长度小于 3 时,情况比较简单,直接遍历数组找到最大的分数即可,因为此时不同分数最多只有两个,按规则应取最大分数作为目标;而当数组长度大于等于 3 时,我们从数组末尾开始,依次比较当前分数与已经找到的 “最大分数”(初始设为数组最后一个元素)。如果当前分数小于这个 “最大分数”,说明找到了一个比之前最大分数稍小一点的分数,此时就将找到的不同分数的个数(用tag变量记录)加 1,并更新 “最大分数” 为当前找到的这个稍小的分数。当tag等于 3 时,就意味着找到了第三大的分数,直接返回即可。如果遍历完整个数组后,tag的值还是 2 或者 1,那就说明没有找到真正意义上的第三大分数,按照规则还是应该返回数组中的最大分数,也就是数组末尾的那个分数。
下面我们来详细解析一下代码:
import java.util.Arrays;
public class Main {
public static int solution(int n, int[] nums) {
int tag = 1;
// 先对分数数组进行排序,使其元素按照从小到大的顺序排列
Arrays.sort(nums);
// 初始时,将结果设为数组中的最大分数,即数组末尾的元素
int result = nums[nums.length - 1];
// 如果数组长度小于3,说明不同分数最多只有两个,按规则取最大分数作为目标
if (nums.length < 3) {
for (int i = nums.length - 1; i >= 0; i--) {
// 遍历数组,找到最大的分数
if (nums[i] > result) {
result = nums[i];
}
}
return result;
} else {
// 当数组长度大于等于3时,从数组末尾开始往前找第三大的分数
for (int i = nums.length - 1; i >= 0; i--) {
// 如果当前分数小于已经找到的“最大分数”(初始为数组最后一个元素)
if (nums[i] < result) {
// 找到一个比之前最大分数稍小的分数,不同分数个数加1
tag++;
// 更新“最大分数”为当前找到的这个稍小的分数
result = nums[i];
}
// 当找到第三大的分数时,返回该分数
if (tag == 3) {
return result;
}
}
// 如果遍历完数组后,tag的值还是2或者1,说明没找到真正意义上的第三大分数
// 按照规则返回数组中的最大分数,即数组末尾的那个分数
if (tag == 2 || tag == 1) {
return nums[nums.length - 1];
}
return 0;
}
}
public static void main(String[] args) {
System.out.println(solution(3, new int[]{3, 2, 1}) == 1);
System.out.println(solution(2, new int[]{1, 2}) == 2);
System.out.println(solution(4, new int[]{2, 2, 3, 1}) == 1);
}
}
在solution方法中,我首先定义了一个变量tag并初始化为 1,它用于记录已经找到的不同分数的个数,然后使用Arrays.sort(nums)对输入的分数数组nums进行排序,这一步是为了后续能方便地从大到小找到目标分数,再然后将result初始化为数组末尾的元素,也就是最初认为的 “最大分数”。
之后通过if-else语句分情况处理:
当nums.length < 3时,进入if分支,通过一个从后往前的循环遍历数组,只要找到比result更大的分数,就更新result的值,最后返回的result就是数组中的最大分数。当nums.length >= 3时,进入else分支,通过另一个从后往前的循环遍历数组。每次遇到比result小的分数,就将tag加 1,并更新result为当前这个较小的分数。当tag等于 3 时,就找到了第三大的分数,直接返回result。如果循环结束后tag的值还是 2 或者 1,就返回数组末尾的分数,也就是最初设定的最大分数。
例如,对于输入n = 3,nums = [3, 2, 1],首先对数组进行排序,得到[1, 2, 3]。然后result初始化为 3,进入else分支的循环。当i = 2时,nums[i] = 1,因为1 < 3,所以tag变为 2,result变为 1。当i = 1时,nums[i] = 2,因为2 > 1,不满足条件。当i = 0时,nums[i] = 1,也不满足条件。循环结束后,tag = 2,按照规则返回数组末尾的分数,也就是 1。
最后我们来看时间复杂度和空间复杂度。从时间复杂度来看,主要的时间消耗在对数组的排序操作和遍历操作上,Arrays.sort方法一般采用的是快速排序等高效排序算法,其平均时间复杂度为O(nlogn),其中n是数组的长度,而后续的遍历操作时间复杂度为O(n),所以总的时间复杂度大致为O(nlogn);在空间复杂度方面,除了输入的分数数组本身占用的空间外,代码中只使用了几个额外的变量(如tag、result等),它们的空间复杂度相对较低,可看作是O(1)。这种算法通过合理的排序和有针对性的遍历判断,有效地解决了 “游戏排名第三大的分数” 这一问题,能够准确地根据给定的分数数组为小 M 确定合适的目标分数。