Medium
题目描述
对于长度为 m 的整数数组 arr 中的元素 x,如果 arr 中超过一半的元素的值为 x,则称 x 为 dominant(主导)元素。
给你一个长度为 n 的下标从 0 开始的整数数组 nums,其中有一个主导元素。
你可以在下标 i 处将 nums 分割成两个数组 nums[0, …, i] 和 nums[i + 1, …, n - 1],但分割只有在以下条件下才有效:
- 0 <= i < n - 1
- nums[0, …, i] 和 nums[i + 1, …, n - 1] 具有相同的主导元素。
这里,nums[i, …, j] 表示 nums 的子数组,该子数组从下标 i 开始到下标 j 结束(包含两端)。特别地,如果 j < i,则 nums[i, …, j] 表示一个空子数组。
返回有效分割的最小下标。如果不存在有效分割,则返回 -1。
示例 1:
输入:nums = [1,2,2,2]
输出:2
解释:我们可以在下标 2 处分割数组,得到数组 [1,2,2] 和 [2]。
在数组 [1,2,2] 中,元素 2 是主导元素,因为它在数组中出现 2 次,且 2 * 2 > 3。
在数组 [2] 中,元素 2 是主导元素,因为它在数组中出现 1 次,且 1 * 2 > 1。
[1,2,2] 和 [2] 都与 nums 有相同的主导元素,因此这是一个有效的分割。
可以证明下标 2 是有效分割的最小下标。
示例 2:
输入:nums = [2,1,3,1,1,1,7,1,2,1]
输出:4
解释:我们可以在下标 4 处分割数组,得到数组 [2,1,3,1,1] 和 [1,7,1,2,1]。
在数组 [2,1,3,1,1] 中,元素 1 是主导元素,因为它在数组中出现 3 次,且 3 * 2 > 5。
在数组 [1,7,1,2,1] 中,元素 1 是主导元素,因为它在数组中出现 3 次,且 3 * 2 > 5。
[2,1,3,1,1] 和 [1,7,1,2,1] 都与 nums 有相同的主导元素,因此这是一个有效的分割。
可以证明下标 4 是有效分割的最小下标。
示例 3:
输入:nums = [3,3,3,3,7,2,2]
输出:-1
解释:可以证明不存在有效的分割。
约束条件:
- 1 <= nums.length <= 10^5
- 1 <= nums[i] <= 10^9
- nums 恰好有一个主导元素。
解题思路
解题思路:
首先理解题意:我们需要找到一个分割点,使得分割后的两个子数组都有相同的主导元素。
解法分析:
预处理阶段:
- 使用哈希表统计整个数组中每个元素的出现频率
- 找到主导元素及其总频率(出现次数超过数组长度的一半)
遍历分割点:
- 对于每个可能的分割点 i(0 到 n-2),计算主导元素在左子数组中的频率
- 右子数组中主导元素的频率 = 总频率 - 左子数组频率
- 检查主导元素在两个子数组中是否都满足主导条件
主导条件判断:
- 对于长度为 len 的子数组,元素频率 > len/2 时该元素为主导元素
- 等价于:频率 * 2 > len
优化点:
- 只需要追踪主导元素的频率变化,不需要统计其他元素
- 一旦找到有效分割点,立即返回(题目要求最小下标)
代码实现
class Solution {
public:
int minimumIndex(vector<int>& nums) {
int n = nums.size();
// 找到主导元素
unordered_map<int, int> freq;
for (int num : nums) {
freq[num]++;
}
int dominant = 0, totalFreq = 0;
for (auto& p : freq) {
if (p.second * 2 > n) {
dominant = p.first;
totalFreq = p.second;
break;
}
}
// 检查每个分割点
int leftFreq = 0;
for (int i = 0; i < n - 1; i++) {
if (nums[i] == dominant) {
leftFreq++;
}
int rightFreq = totalFreq - leftFreq;
int leftLen = i + 1;
int rightLen = n - i - 1;
// 检查两个子数组是否都有主导元素
if (leftFreq * 2 > leftLen && rightFreq * 2 > rightLen) {
return i;
}
}
return -1;
}
};
class Solution:
def minimumIndex(self, nums: List[int]) -> int:
n = len(nums)
# 找到主导元素
freq = {}
for num in nums:
freq[num] = freq.get(num, 0) + 1
dominant = 0
total_freq = 0
for num, count in freq.items():
if count * 2 > n:
dominant = num
total_freq = count
break
# 检查每个分割点
left_freq = 0
for i in range(n - 1):
if nums[i] == dominant:
left_freq += 1
right_freq = total_freq - left_freq
left_len = i + 1
right_len = n - i - 1
# 检查两个子数组是否都有主导元素
if left_freq * 2 > left_len and right_freq * 2 > right_len:
return i
return -1
public class Solution {
public int MinimumIndex(IList<int> nums) {
int n = nums.Count;
// 找到主导元素
Dictionary<int, int> freq = new Dictionary<int, int>();
foreach (int num in nums) {
freq[num] = freq.GetValueOrDefault(num, 0) + 1;
}
int dominant = 0, totalFreq = 0;
foreach (var pair in freq) {
if (pair.Value * 2 > n) {
dominant = pair.Key;
totalFreq = pair.Value;
break;
}
}
// 检查每个分割点
int leftFreq = 0;
for (int i = 0; i < n - 1; i++) {
if (nums[i] == dominant) {
leftFreq++;
}
int rightFreq = totalFreq - leftFreq;
int leftLen = i + 1;
int rightLen = n - i - 1;
// 检查两个子数组是否都有主导元素
if (leftFreq * 2 > leftLen && rightFreq * 2 > rightLen) {
return i;
}
}
return -1;
}
}
var minimumIndex = function(nums) {
const n = nums.length;
// 找到主导元素
const freq = new Map();
for (const num of nums) {
freq.set(num, (freq.get(num) || 0) + 1);
}
let dominant = 0, totalFreq = 0;
for (const [num, count] of freq) {
if (count * 2 > n) {
dominant = num;
totalFreq = count;
break;
}
}
// 检查每个分割点
let leftFreq = 0;
for (let i = 0; i < n - 1; i++) {
if (nums[i]
复杂度分析
| 复杂度 | 大小 |
|---|---|
| 时间复杂度 | O(n) |
| 空间复杂度 | O(k) |
其中 n 是数组长度,k 是不同元素的个数。在最坏情况下 k = n,所以空间复杂度为 O(n)。
相关题目
. Majority Element (Easy)