A. 牛牛爱奇数
题目描述
在牛牛面前放着 n 个数,这些数字既有奇数也有偶数,只不过牛牛对奇数情有独钟,他特别想让这些数都变成奇数。
现在牛牛获得了一种能力,他可以执行一种操作:每次选中一个偶数,然后把这些数中与该数相等的数都除以 2,例如现在有一个数组为 [2, 2, 3],那么牛牛可以执行一次操作,使得这个数组变为 [1, 1, 3]。
牛牛现在想知道,对于任意的 n 个数,他最少需要操作多少次,使得这些数都变成奇数?
备注:
1 ≤ n ≤ 10^6,代表一个有多少数字
a1, a2, a3 ... an(1 ≤ ai ≤ 10^9)代表数字的大小
示例1
输入
3,[2,2,3]
输出
1
说明
只需做一次操作,会将其中的偶数2都变成1,满足了所有的数都是奇数的要求。
示例2
输入
3,[1,3,7]
输出
0
说明
不需要做任何操作,因为所有的数原本就是奇数。
解法:大顶堆 + 哈希集合
思路分析
每次可以让所有相同的偶数变成一半,问最少操作次数。
一次操作所有相同数,显然要使用哈希集合进行去重。
而要使得操作次数最少,必然对偶数从大到小进行操作,这样才可以避免对某个偶数进行重复的操作。
想要从大到小操作,显然使用大顶堆保存偶数。每次从堆顶取一个数字进行操作,若得到的新的数字是偶数并且未在哈希集合中出现过,就把它再加入大顶堆中。
时间复杂度:O(max(n, logm))。这里的 m 是最大的偶数。遍历一遍数字需要 O(n),把偶数全变成奇数需要 O(logm)。
空间复杂度:O(n)。
代码实现
public int solve (int n, int[] a) {
Set<Integer> set = new HashSet();
int ans = 0;
for(int i: a)
if((i & 1) == 0) set.add(i);
Queue<Integer> q = new PriorityQueue<Integer>((x,y) -> y - x);
q.addAll(set);
while(!q.isEmpty()) {
int num = q.poll() >> 1;
ans++