题目链接:leetcode747
题目大意
解题思路
问题中有说一定存在最大值,意思是这个最大值是唯一的。确定了问题主要是寻找最大值和次大值就可以愉快地解决了,主要思路有如下两种:
线性扫描
直接 for 一遍即可,如果只是考虑比较的次数,最差会达到 2 ∗ n − 2 2*n-2 2∗n−2
时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)
锦标赛树
这个树是树形选择排序的前置技能,也是堆排序的弱化版,因为空间复杂度较高,但优点是能够减少比较次数。对于本题,仅仅只要寻找最大值和次大值,构造的树没必要为一个严谨的满二叉树,利用数组像堆那样构造即可,父节点维护子节点的最大值。找次大值时只需要沿着最大值的根到叶子的路径中,每个节点的兄弟节点,取最大的一个即为次大值。这个算法如果没有最大值是唯一的约束则不成立,因为那样路径就不能保证唯一。
时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)
考虑比较的次数,这个算法最差会达到 n + ⌈ l o g n ⌉ − 2 n+\lceil logn \rceil-2 n+⌈logn⌉−2。如何计算呢?我们容易计算,构造这个树并找到最大值的复杂度为 n − 1 n-1 n−1,之后根据最大值的路径去寻找次大值的次数为 h − 1 h-1 h−1,h为树的高度,减一是因为根已经是最大值了,故不用考虑
代码
class Solution {
public:
int dominantIndex(vector<int>& nums) {
if(nums.size()==1) return 0;
int tree[nums.size()<<1], n=nums.size();
for (int i=n; i<2*n; i++) tree[i]=i-n;
for (int i=2*n-2; i>=2; i-=2) tree[i>>1]=nums[tree[i]]>nums[tree[i|1]]?tree[i]:tree[i|1];
int first_max_id = tree[1], second_max_id, start=2;
if (nums[tree[1]] == nums[tree[2]]) second_max_id=tree[3];
else start=3, second_max_id=tree[2];
for (int i=2*start; i<=2*n-2; i*=2) {
if (nums[tree[i]]==nums[tree[i>>1]]) second_max_id=nums[tree[i|1]]>nums[second_max_id]?tree[i|1]:second_max_id;
else second_max_id = nums[tree[i]]>nums[second_max_id]?tree[i++]:second_max_id;
}
return nums[first_max_id]>=nums[second_max_id]*2?first_max_id:-1;
}
};