java数据结构与算法刷题-----LeetCode462. 最小操作次数使数组元素相等 II

java数据结构与算法刷题目录(剑指Offer、LeetCode、ACM)-----主目录-----持续更新(进不去说明我没写完):https://blog.csdn.net/grd_java/article/details/123063846

在这里插入图片描述

你必须知道的数学知识:给你n个数,每个数字都可以不断进行+1和-1操作,让这n个数字通过若干次+1或-1操作使其全部相同。最少的操作次数方案为,找到这n个数的中位数,然后所有其它数字通过+1和-1操作向中位数看齐。

快速选择算法找中位数

很多同学会直接排序,然后找到中位数,然后依次遍历进行+1和-1操作。但是直接排序就需要直接用快速排序算法。时间复杂度还不如用小根堆来的快呢。毕竟小根堆也是专门处理这类的问题的好手。而快速排序是将每一个数的位置找到,我们这里的需求只是找第k个数。

解题思路:时间复杂度O( n n n),空间复杂度O( l o g 2 n log_2n log2n)
  1. 中位数就相当于找第k大数(k是n个数的中间位置),那么很多小伙伴会直接想到堆排序(小根堆)。但是堆排序它更适合将前K大的数都找出来,而只是找一个数字,快速选择更好。因为堆排序对k-1,k-2等数都会进行操作。有兴趣的可以参考215题
🏆LeetCode215. 数组中的第K个最大元素https://blog.csdn.net/grd_java/article/details/136932454
  1. 215题中,其中一个方法就是使用快速选择算法找第k个数,因此这里不再赘述快速选择的逻辑
  2. 我们的核心就是,先通过快速选择算法,找到中位数,最后其它数字全部通过+1和-1操作,让其和中位数相等。
代码

在这里插入图片描述

class Solution {
    public int minMoves2(int[] nums) {
        int n = nums.length;
        int ans = 0;
        // 下面代码和int k = n/2+1;效果一样,但是我们还是严谨点好
        int k = n % 2 == 0 ? (n+2) / 2 : (n + 1) / 2;//获取nums数组中,中位数应该是第几个数。这里规定,如果nums有偶数个元素,取中间第二个
        //这里我们不进行排序,转而通过快速选择算法,找到数组中第k大数
        int h = quickSort(nums,0,n-1,k);//快速选择(省略快速排序的排序操作,全力集中于用快排的思想找到第k大数)
        for(int num : nums){//让若干数字都变成一样的,每次只能加一或减一,最少的操作次数方案为。将所有数字排序,选择中间大的数,所以数字与它看齐
            ans += Math.abs(h - num);//所有数字与中位数看齐
        }
        return ans;
    }
    //经典的快速选择实现,while{do..while;do..while;if}
    int quickSort(int[] a, int left,int right,int k){
        if(left >= right) return a[left];
        int x = a[left + right >> 1];//获取当前区间中间的数x
        //借助两个下标,让比x小的去x左边,比x大的去右边
        int leftIndex = left - 1, rightIndex = right + 1;//分别代表区间中应该在x左边(<=x)的下标和x右边的下标
        while(leftIndex < rightIndex){
            do leftIndex++;while(a[leftIndex] < x);//当leftIndex指向的值,>=x时,终止循环
            do rightIndex--;while(a[rightIndex] > x);//当rightIndex指向的值,<=x时,终止循环
            if(leftIndex < rightIndex){//如果leftIndex目前在x左边,而且rightIndex目前在x右边,说明他俩的指向的值应该调换位置
                swap(a,leftIndex,rightIndex);//让小的去x左边leftIndex位置,大的去x右边rightIndex位置
            }
        }//直到目前区间实现x是中位数,左边都比x小,右边都比x大为止
        //此时rightIndex必然指向第一个比x小的值(也就是x比它小的左边部分的边界)
        if(k <= (rightIndex - left + 1)) //如果left到rightIndex区间(比x小的左边部分),正好够我们找到第k大数
            return quickSort(a,left,rightIndex,k);//进入左边区间继续寻找
        else //如果左边部分没有第k大数,那就去右边找,此时左边部分的 rightIndex - left + 1已经确定都比第k大数小,所以k - 左边部分为下轮右边部分该找的值
            return quickSort(a,rightIndex+1,right,k-(rightIndex-left+1));//右边部分找第(k - 左边部分个数)大的数。
    }
    void swap(int[] a,int i,int j){
        int t = a[i];
        a[i] = a[j];
        a[j] = t;
    }
}
  • 25
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

殷丿grd_志鹏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值