Leetcode 6022. 将数组和减半的最少操作次数
Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
1、题目📑
给你一个正整数数组 nums 。每一次操作中,你可以从 nums 中选择 任意 一个数并将它减小到 恰好 一半。(注意,在后续操作中你可以对减半过的数继续执行操作)
请你返回将 nums 数组和 至少 减少一半的 最少 操作数。
实例1:
输入:nums = [5,19,8,1]
输出:3
解释:初始 nums 的和为 5 + 19 + 8 + 1 = 33 。
以下是将数组和减少至少一半的一种方法:
选择数字 19 并减小为 9.5 。
选择数字 9.5 并减小为 4.75 。
选择数字 8 并减小为 4 。
最终数组为 [5, 4.75, 4, 1] ,和为 5 + 4.75 + 4 + 1 = 14.75 。
nums 的和减小了 33 - 14.75 = 18.25 ,减小的部分超过了初始数组和的一半,18.25 >= 33/2 = 16.5 。
我们需要 3 个操作实现题目要求,所以返回 3 。
可以证明,无法通过少于 3 个操作使数组和减少至少一半。
实例2:
输入:nums = [3,8,20]
输出:3
解释:初始 nums 的和为 3 + 8 + 20 = 31 。
以下是将数组和减少至少一半的一种方法:
选择数字 20 并减小为 10 。
选择数字 10 并减小为 5 。
选择数字 3 并减小为 1.5 。
最终数组为 [1.5, 8, 5] ,和为 1.5 + 8 + 5 = 14.5 。
nums 的和减小了 31 - 14.5 = 16.5 ,减小的部分超过了初始数组和的一半, 16.5 >= 31/2 = 16.5 。
我们需要 3 个操作实现题目要求,所以返回 3 。
可以证明,无法通过少于 3 个操作使数组和减少至少一半。
限制:
1 <= nums.length <= 1051 <= nums[i] <= 107
2、思路🧠
方法一:优先队列
- 将数组按照大根堆的形式放入,每次对最大的数进行减半的操作,要求数组和恰好减到恰好一半并且最少的操作数。
- 求减少一半的 最少 操作数,肯定是对最大数进行减半的操作有最大的收益。
废话少说~~~~~上代码!
3、代码👨💻
第一次commit AC
class Solution {
public int halveArray(int[] nums) {
PriorityQueue<Double> q = new PriorityQueue<>(nums.length, new Comparator<Double>() {
@Override
public int compare(Double o1, Double o2) {
int temp = (o2 - o1) > 0 ? 1 : - 1;
return temp;
}
});
double sum = 0;
for(int i = 0; i < nums.length; i++){
sum+=nums[i];
q.offer(nums[i] * 1.0);
}
double now = sum / 2.0;
int count = 0;
while(now < sum) {
double temp = q.poll();
q.offer(temp / 2.0);
sum -= temp / 2.0;
count++;
}
return count;
}
}
时间复杂度:O(N) N为数组 nums 的长度
空间复杂度:O(N)
4、总结
该题目的对大根堆的结构进行灵活的运用,并且能够想到对最大数减半操作收益是最大的。 自建堆模板:
package com.cz.Sort;
import java.util.Arrays;
/**
* @ProjectName: 大根堆
* @Package: com.cz.Sort
* @ClassName: Heap
* @Author: 张晟睿
* @Date: 2022/3/17 20:23
* @Version: 1.0
*/
public class Heap {
public static class MyMaxheap {
private int heap[];
private final int limit;
private int heapSize;
public MyMaxheap(int limit) {
heap = new int [limit];
this.limit = limit;
heapSize = 0;
}
public boolean isEmpty() {
return heapSize == 0;
}
public boolean isFull() {
return heapSize == limit;
}
public void push(int value){
if (isFull())
throw new RuntimeException("heap is full");
heap[heapSize] = value;
heapInsert(heap, heapSize++);
}
public int pop(){
int ans = heap[0];
swap(heap, 0 , --heapSize);
heapify(heap, 0, heapSize);
return ans;
}
private void heapInsert(int[] arr, int index) {
//当前index和父亲比较 当前index比其父亲大,交换
//当前来的值和他的父亲节点比较
//结束条件
//arr[index] 不比 arr[index父] 大的时候
//index = 0 来到了顶端
while(arr[index] > arr[(index - 1) / 2]) {
swap(arr, index , (index - 1) / 2);
index = (index - 1) / 2;
}
}
/**
*
* @param arr 需要调整为大根堆的数组
* @param index 从index位置往下看,不断的下沉 停止条件:当前所有的孩子都不比我大,没有孩子了
* @param heapSize 堆的大小
*/
private void heapify(int[] arr, int index, int heapSize) {
int left = 2 * index ^ 1; //左孩子的下标
while(left < heapSize) { //左孩子下标不越界,即有孩子存在
//左右两个孩子,谁大把谁给largest
//右孩子胜出的情况下 ->1.有右孩子 2.有孩子的值比左孩子的值大
//否则,就是左孩子胜出
// 右孩子胜出 left + 1 < heapSize 右孩子不越界 arr[left + 1] > arr[left] 右孩子的值大于左孩子的值
int largest = left + 1 < heapSize && arr[left + 1] > arr[left] ? left + 1 : left;
//largest两个孩子中最大孩子的下标
//最大孩子的值和父亲的值进行pk 谁大把谁的下标给largest
largest = arr[largest] > arr[index] ? largest : index;
//两个孩子都pk不过父节点,此时不用下沉了
if(largest == index) break;
//largest,index交换
swap(arr, largest, index);
//index继续往下沉,继续找下一个左孩子
index = largest;
left = (2 * index) ^ 1;
}
}
public void swap(int arr[], int i, int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
public static void main(String[] args) {
MyMaxheap mmp = new MyMaxheap(10);
// for (int i = 0; i < 1000; i++) {
// if (mmp.isFull()) break;
// mmp.push((int)(Math.random() * 100));
// }
// mmp.push(1);
// mmp.push(3);
// mmp.push(6);
// mmp.push(2);
// mmp.push(5);
// mmp.push(9);
// System.out.println(Arrays.toString(mmp.heap));
// System.out.println(mmp.pop());
int arr[] = new int[]{1,3,6,2,5,9};
for (int i = arr.length - 1; i >= 0; i--) {
mmp.heapify(arr, i , arr.length);
}
}
}
❤️来自专栏《LeetCode基础算法题》欢迎订阅❤️
厂长写博客目的初衷很简单,希望大家在学习的过程中少走弯路,多学一些东西,对自己有帮助的留下你的赞赞👍或者关注➕都是对我最大的支持,你的关注和点赞给厂长每天更文的动力。
对文章其中一部分不理解,都可以评论区回复我,我们来一起讨论,共同学习,一起进步!