Loading...

两种方法解决「将数组和减半的最少操作次数」

在这里插入图片描述 解决这个问题的关键是:每次必须选择当前最大的元素减半。

如果要高效地 “每次获取最大元素”,大根堆无疑是最佳选择。

方法 1:用 PriorityQueue 实现

使用 Java 内置的PriorityQueue作为大根堆,直接存储double类型的元素。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public static int halveArray(int[] nums) {
    // 大根堆:通过比较器(b.compareTo(a))实现,确保堆顶是最大元素
    PriorityQueue<Double> heap = new PriorityQueue<>((a, b) -> b.compareTo(a));
    double sum = 0;
    
    // 计算数组总和,并将所有元素加入大根堆
    for (int num : nums) {
        heap.add((double) num);
        sum += num;
    }
    
    // 减少的总量至少为原总和的一半
    sum /= 2;
    int ans = 0;
    double minus = 0; // 累计减少的总量
    
    // 每次取最大元素减半,直到累计减少量≥目标
    while (minus < sum) {
        ans++; // 操作次数+1
        double cur = heap.poll() / 2; // 取出最大元素,减半
        minus += cur; 
        heap.add(cur); // 减半后的元素放回堆中
    }
    
    return ans;
}

方法 2:自定义大根堆(用整数避免浮点数精度问题)

使用自定义的大根堆,并且用long类型存储元素(通过左移 20 位放大元素),避开浮点数精度陷阱。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public int halveArray(int[] nums) {
		int n = nums.length;
		long[] heap = new long[n];
		long sum = 0;

		for (int i = n - 1; i >= 0; i--) {
			// 放大2^20倍(足够覆盖1e9的精度,避免浮点数)
			heap[i] = (long) nums[i] << 20;
			sum += heap[i];
			heapify(heap, n, i);
		}

		sum /= 2;
		int ans = 0;
		long minus = 0;

		while (minus < sum) {
			ans++;
			// 堆顶减半(整数操作,无精度问题)
			heap[0] >>= 1; // 右移1位 = 除以2,比/=2更快
			minus += heap[0];
			// 调整堆顶
			heapify(heap, n, 0);
		}

		return ans;
	}

	private void heapify(long[] heap, int size, int i) {
		// 暂存当前节点值,避免重复访问heap[i]
		long curVal = heap[i];
		// 左孩子索引
		int left = i * 2 + 1;

		while (left < size) {
			// 暂存左孩子值,减少数组访问
			long leftVal = heap[left];
			// 右孩子值:存在则取,不存在则设为极小值(不影响比较)
			long rightVal = (left + 1 < size) ? heap[left + 1] : Long.MIN_VALUE;

			//选左右孩子中更大的那个
			int largerChild = (rightVal > leftVal) ? left + 1 : left;
			long largerVal = (largerChild == left) ? leftVal : rightVal;

			//若当前节点值 >= 较大孩子值,堆结构已满足,退出
			if (curVal >= largerVal) {
				break;
			}

			//直接将较大孩子值覆盖到当前节点
			heap[i] = largerVal;
			// 更新当前节点索引,继续向下调整
			i = largerChild;
			left = i * 2 + 1;
		}

		//将暂存的当前节点值放到最终位置
		heap[i] = curVal;
	}

完整代码 链接:https://pan.quark.cn/s/2ed207e367ac 提取码:3H47

最后更新于 2026-04-05 17:35:33
Code Road Record