Easy
题目描述
给你一个非递减的有序整数数组,已知这个数组中恰好有一个整数,它的出现次数超过数组元素总数的 25%,请你找到并返回这个整数。
示例 1:
输入:arr = [1,2,2,6,6,6,6,7,10]
输出:6
示例 2:
输入:arr = [1,1]
输出:1
提示:
1 <= arr.length <= 10^40 <= arr[i] <= 10^5
思路提示:
- 将数组分成四部分 [1-25%] [25-50%] [50-75%] [75%-100%]
- 答案应该在某个区间的端点处
- 可以用二分查找来检查某个元素的出现频率
解题思路
解题思路
这道题有几种解法:
方法一:直接遍历统计(简单)
最直观的方法是遍历数组统计每个元素的出现次数,当某个元素的出现次数超过 n/4 时返回该元素。
方法二:候选点检查(推荐)
根据题目提示,我们可以利用数组有序的特性。如果一个元素出现次数超过25%,那么它必然会出现在以下位置之一:
arr[n/4]:第一个四分位点arr[n/2]:中位数arr[3*n/4]:第三个四分位点
我们只需要检查这三个候选位置的元素,然后用二分查找计算它们的出现次数即可。
方法三:滑动窗口
由于数组有序,我们可以用滑动窗口的思想。如果某个元素出现次数超过25%,那么在长度为 n/4 + 1 的窗口中,首尾元素必然相等。
这里我们采用候选点检查的方法,因为它既高效又易于实现。
代码实现
class Solution {
public:
int findSpecialInteger(vector<int>& arr) {
int n = arr.size();
int threshold = n / 4;
// 候选位置:1/4, 1/2, 3/4 处的元素
vector<int> candidates = {arr[n/4], arr[n/2], arr[3*n/4]};
for (int candidate : candidates) {
// 使用二分查找找到第一个和最后一个出现位置
int left = lower_bound(arr.begin(), arr.end(), candidate) - arr.begin();
int right = upper_bound(arr.begin(), arr.end(), candidate) - arr.begin();
if (right - left > threshold) {
return candidate;
}
}
return -1; // 理论上不会到这里
}
};
class Solution:
def findSpecialInteger(self, arr: List[int]) -> int:
n = len(arr)
threshold = n // 4
# 候选位置:1/4, 1/2, 3/4 处的元素
candidates = [arr[n//4], arr[n//2], arr[3*n//4]]
for candidate in candidates:
# 使用二分查找找到第一个和最后一个出现位置
left = bisect.bisect_left(arr, candidate)
right = bisect.bisect_right(arr, candidate)
if right - left > threshold:
return candidate
return -1 # 理论上不会到这里
public class Solution {
public int FindSpecialInteger(int[] arr) {
int n = arr.Length;
int threshold = n / 4;
// 候选位置:1/4, 1/2, 3/4 处的元素
int[] candidates = {arr[n/4], arr[n/2], arr[3*n/4]};
foreach (int candidate in candidates) {
// 使用二分查找找到第一个和最后一个出现位置
int left = Array.BinarySearch(arr, candidate);
if (left < 0) left = ~left;
else {
// 找到第一个出现位置
while (left > 0 && arr[left - 1] == candidate) left--;
}
int right = left;
while (right < n && arr[right] == candidate) right++;
if (right - left > threshold) {
return candidate;
}
}
return -1; // 理论上不会到这里
}
}
/**
* @param {number[]} arr
* @return {number}
*/
var findSpecialInteger = function(arr) {
const n = arr.length;
const threshold = Math.floor(n / 4);
// 候选位置:1/4, 1/2, 3/4 处的元素
const candidates = [arr[Math.floor(n/4)], arr[Math.floor(n/2)], arr[Math.floor(3*n/4)]];
for (const candidate of candidates) {
// 找到第一个出现位置
let left = 0;
while (left < n && arr[left] < candidate) left++;
// 找到最后一个出现位置
let right = left;
while (right < n && arr[right]
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 候选点检查 | O(log n) | O(1) |
| 直接遍历 | O(n) | O(1) |
| 滑动窗口 | O(n) | O(1) |
推荐使用候选点检查方法,时间复杂度最优。