【LeetCode每日一题】798、得分最高的最小轮调

本文探讨了一种高效算法,用于在给定数组nums中找到通过最小轮调次数k,使得数组中小于等于其索引的元素计分最高。作者介绍了暴力解法的O(n^2)复杂度,并详细阐述了使用差分数组优化到O(n)的时间复杂度的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

798.、得分最高的最小轮调
给你一个数组nums,我们可以将它按一个非负整数 k 进行轮调,这样可以使数组变为[nums[k], nums[k + 1], … nums[nums.length - 1], nums[0], nums[1], …, nums[k-1]]的形式。此后,任何值小于或等于其索引的项都可以记作一分。例如,数组为nums = [2,4,1,3,0],我们按k = 2进行轮调后,它将变成[1,3,0,2,4]。这将记为 3 分,因为 1 > 0 [不计分]、3 > 1 [不计分]、0 <= 2 [计 1 分]、2 <= 3 [计 1 分],4 <= 4 [计 1 分]。在所有可能的轮调中,返回我们所能得到的最高分数对应的轮调下标 k 。如果有多个答案,返回满足条件的最小的下标 k 。
感受:今天这个太困难了,使用暴力解法自己做出来还是没有问题的,但是时间复杂度是O(n^2);题解差分法的思路勉强看懂了,浅学一下哎。。。

	/*
        最简单的做法是遍历每个可能的k,计算轮调k个位置之后的数组得分。
        假设数组的长度是n,则有n种可能的轮调,对于每种轮调都需要O(n)的时间计算得分,
        总时间复杂度是 O(n^2)。
     */
    public int bestRotation(int[] nums) {
        int n = nums.length;
        int min = -1;
        int maxPoint = Integer.MIN_VALUE;
        if (nums == null || nums.length == 0) {
            return min;
        }
        for (int i = 0; i < n - 1; i++) {
            int cur = 0;
            for (int j = 0; j < n; j++) {
                //小于等于记一分,大于不计分
                cur += nums[i] > (j + n - i) % n ? 0 : 1;
            }
            if (cur > maxPoint) {
                maxPoint = cur;
                min = i;
            }
        }
        return min;
    }
    /*
        对于数组nums中的元素x,当x所在下标大于或等于x时,元素x会记1分。
        因此元素x记1分的下标范围是 [x, n - 1],有 n - x个下标,
        元素x不计分的下标范围是 [0, x - 1],有x个下标。
        假设元素x的初始下标为i,则当轮调下标为k时,元素x位于下标(i - k + n) mod n。
        如果元素x记1分,则有(i - k + n) mod n ≥ x,等价于k ≤ (i−x+n) mod n。
        由于元素x记1分的下标有 n - x个,因此有k ≥ (i+1) mod n。
        将取模运算去掉之后,可以得到k的实际取值范围:
        当 i < x时,i+1≤k≤i−x+n;
        当 i ≥ x 时,k≥i+1 或 k≤i−x。
        对于数组nums中的每个元素,都可以根据元素值与元素所在下标计算该元素记1分的轮调下标范围。
        遍历所有元素之后,即可得到每个轮调下标对应的计1分的元素个数,计1分的元素个数最多的轮调下标即为得分最高的轮调下标。
        如果存在多个得分最高的轮调下标,则取其中最小的轮调下标。
        创建长度为n的数组points,其中points[k]表示轮调下标为k时的得分。
        对于数组nums 中的每个元素,得到该元素记1分的轮调下标范围,然后将数组points的该下标范围内的所有元素加1。
        当数组points中的元素值确定后,找到最大元素的最小下标。该做法的时间复杂度仍然是 O(n^2),为了降低时间复杂度,需要利用差分数组。
        假设元素x的初始下标为i。当 i < x时应将points的下标范围 [i + 1, i - x + n]内的所有元素加1,
        当i≥x时应将points的下标范围[0, i + 1]和 [i - x, n - 1]内的所有元素加1。
        由于是将一段或两段连续下标范围内的元素加1,因此可以使用差分数组实现。
        定义长度为n的差分数组diffs,其中diffs[k]=points[k]−points[k−1]
        (特别地,points[−1]=0),具体做法是:令low=(i+1) mod n,high=(i−x+n+1) mod n,
        将diffs[low] 的值加1,将diffs[high] 的值减1,如果low≥high 则将diffs[0]的值加1。
        遍历数组nums的所有元素并更新差分数组之后,遍历数组diffs并计算前缀和,
        则每个下标处的前缀和表示当前轮调下标处的得分。在遍历过程中维护最大得分和最大得分的最小轮调下标,遍历结束之后即可得到结果。
        实现方面,不需要显性创建数组points,只需要创建差分数组diffs,遍历数组diffs时即可根据前缀和得到数组points中的每个元素值。
     */
    public int bestRotation(int[] nums) {
        int n = nums.length;
        int[] diffs = new int[n];
        for (int i = 0; i < n; i++) {
            int low = (i + 1) % n;
            int high = (i - nums[i] + n + 1) % n;
            diffs[low]++;
            diffs[high]--;
            if (low >= high) {
                diffs[0]++;
            }
        }
        int bestIndex = 0;
        int maxScore = 0;
        int score = 0;
        for (int i = 0; i < n; i++) {
            score += diffs[i];
            if (score > maxScore) {
                bestIndex = i;
                maxScore = score;
            }
        }
        return bestIndex;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值