统计班级中的说谎者
问题描述
在小C的班级里,有 N 个学生,每个学生的成绩是 A_i。小C发现了一件有趣的事:当且仅当某个学生的成绩小于或等于自己的有更多人时,这个学生会说谎。换句话说,如果分数小于等于他的学生数量大于比他分数高的学生数量,则他会说谎。
现在,小C想知道班里有多少个学生会说谎。
测试样例
样例1:
输入:
A = [100, 100, 100]
输出:3
样例2:
输入:
A = [2, 1, 3]
输出:2
样例3:
输入:
A = [30, 1, 30, 30]
输出:3
样例4:
输入:
A = [19, 27, 73, 55, 88]
输出:3
样例5:
输入:
A = [19, 27, 73, 55, 88, 88, 2, 17, 22]
输出:5
问题分析
这道题目要求我们找出班级中会说谎的学生数量。根据题目描述,一个学生会说谎的条件是:成绩小于等于他的学生人数大于成绩比他高的学生人数。
解题思路
方法一:直接遍历(O(n²))
最直观的解法是对每个学生,遍历所有其他学生来统计分数关系。
int n = A.length;
int liars = 0;
for (int i = 0; i < n; i++) {
int lessOrEqual = 0;
int greater = 0;
// 统计与当前学生的分数关系
for (int j = 0; j < n; j++) {
if (A[j] <= A[i]) lessOrEqual++;
else greater++;
}
if (lessOrEqual > greater) liars++;
}
return liars;
}
方法二:排序优化(O(n log n))
通过排序可以更高效地统计分数关系。
int n = A.length;
int[] sorted = A.clone();
Arrays.sort(sorted);
int liars = 0;
for (int score : A) {
// 二分查找当前分数位置
int pos = Arrays.binarySearch(sorted, score);
// 处理重复分数
while (pos < n - 1 && sorted[pos + 1] == score) {
pos++;
}
int lessOrEqual = pos + 1;
int greater = n - lessOrEqual;
if (lessOrEqual > greater) liars++;
}
return liars;
}
复杂度分析
方法一:
-
时间复杂度:O(n²)
-
空间复杂度:O(1)
方法二:
-
时间复杂度:O(n log n)
-
空间复杂度:O(n)
示例分析
让我们分析样例:[19, 27, 73, 55, 88]
-
对于19:
-
小于等于19的有:19 (1个)
-
大于19的有:27,73,55,88 (4个)
-
1 < 4,不说谎
-
对于27:
-
小于等于27的有:19,27 (2个)
-
大于27的有:73,55,88 (3个)
-
2 < 3,不说谎
-
对于73:
-
小于等于73的有:19,27,73,55 (4个)
-
大于73的有:88 (1个)
-
4 > 1,说谎
依此类推...
注意事项
- 处理重复分数:
-
当有多个相同分数时,需要正确计算位置
-
二分查找后需要处理重复元素
-
边界情况:
-
所有分数相同的情况
-
只有一个学生的情况
-
计数判断:
-
必须严格大于,等于不算说谎
-
包含自己的分数在内
总结
-
这道题目可以用多种方法解决,关键是找到高效的方式来统计分数关系。
-
虽然直接遍历方法简单直观,但在处理大规模数据时效率较低。
-
排序优化的方法虽然需要额外空间,但大大提高了时间效率。
-
实际应用中,如果数据规模较大,建议使用排序优化的方法。