LeetCode c++--1551. 使数组中所有元素相等的最小操作数

题目描述:

存在一个长度为 n 的数组 arr ,其中 arr[i] = (2 * i) + 1 ( 0 <= i < n )。

一次操作中,你可以选出两个下标,记作 x 和 y ( 0 <= x, y < n )并使 arr[x] 减去 1 、arr[y] 加上 1 (即 arr[x] -=1 且 arr[y] += 1 )。最终的目标是使数组中的所有元素都 相等 。题目测试用例将会 保证 :在执行若干步操作后,数组中的所有元素最终可以全部相等。

给你一个整数 n,即数组的长度。请你返回使数组 arr 中所有元素相等所需的 最小操作数 。

 

示例 1:

输入:n = 3
输出:2
解释:arr = [1, 3, 5]
第一次操作选出 x = 2 和 y = 0,使数组变为 [2, 3, 4]
第二次操作继续选出 x = 2 和 y = 0,数组将会变成 [3, 3, 3]
示例 2:

输入:n = 6
输出:9
 

提示:

1 <= n <= 10^4

解题:

1.直接计算

class Solution {
public:
    /*
        目的:将数组中的数都变为中位数需要进行交换的次数
        当 n :
            1.n == 1:cout<<0;
            2.为奇数时,从(n-1)/2向外扩展 i++,j--,累加到 l = (arr[i] - arr[j])/2;
            3.为偶数时,从中间 i = n / 2; j = ( n - 2 ) / 2; 累加到 l = (arr[i] - arr[j])/2;
   */
    int minOperations(int n) {
        if(n == 1)return 0;
        vector<int> arr(n,0); //创建arr空间为n,并初始化为0
        unsigned int i,l = 0,j;
        //将arr[i] == (2*i)+1进行保存
        for(i = 0; i < n ; i ++)
        {
            arr[i] = ( 2 * i ) + 1 ;  
        }
        //奇数操作
        if(n % 2 == 1)
            for(i = ( n-1 )/2, j = i; i < n ; i++ , j--)//j 到达 0 的同时 i 到达 n-1
            {
                l += (arr[i]-arr[j])/2;
            }
        else      //偶数操作
            for(i = n/2,j = (n-2)/2; i < n ; i++ , j--)
                {
                    l += (arr[i]-arr[j])/2;
                    //cout <<l<<" " ;
                 }
        return l;
    }
};

时间复杂度o(n),空间复杂度o(1)

2.

class Solution {
public:
    int minOperations(int n) {
    //初始化
        vector<int> a(n);
        int sum = 0;
        for (int i = 0; i < n; ++i)
        { //计算数据
            a[i] = 2*i+1;
        //计算总和
            sum += a[i];
        }
        //计算平均
        sum /= n;
        int ans = 0;
        for (int i = 0; i < n; ++i)
        {
        //计算操作次数x2
            ans += abs(sum-a[i]);
        }
        return ans/2;
    }
};        

时间复杂度o(n),空间复杂度o(1)

3.取巧,从数据中找规律

(本方法借鉴于力扣)作者:zyoung1024
链接:https://leetcode-cn.com/problems/minimum-operations-to-make-array-equal/solution/deng-chai-shu-lie-mo-ni-he-shu-xue-fang-fa-xiang-j/

(1)

拿到这道题,感觉有点绕,仔细分析发现arr[i] = (2 * i) + 1 (0 <= i < n)是典型的等差数列(1,3,5,7,9...).
根据等差数列的求和公式,很容易求出数组arr的元素总和是n^2.
题设中说每次操作选出两个下标x y使arr[x]减一arr[y]加一.换句话说,无论怎样选择x y,无论操作多少次,数组的总和不会变.
题设又保证数组中所有元素最终可以全部相等.
那我们假设最终所有元素等于a那么n*a == n^2,所以a == n,也就是说最终数组元素都是n.其实n是数组的平均值.知道最终元素都是n后,通过从数组起始和末尾下标开始向中间遍历,就可以到达操作数最小.
假设左边的下标是i ((2 * i) + 1 < n)那么相应右边的下标是n - i.相应两个元素值与n的差都是n - 1 + 2 * i.所以我们只要计算数组中值小于n的元素与n的差的总和,就得到最小操作数了.

 int minOperations(int n) {
    int operation = 0;
    for(int i = 1; i < n ; i += 2) {
        operation += (n - i);
    }
    return operation;
}
// 时间复杂度是O(n) 空间复杂度是O(1)

(2)

因为是等差数列,很可能找到一个数学公式,用O(1)的时间复杂度解决.
先举几个简单的例子找找规律

n=3 最小操作数是 2
n=4 最小操作数是 1 + 2
n=5 最小操作数是 2 + 4
n=6 最小操作数是 1 + 3 + 5
n=7 最小操作数是 2 + 4 + 6
果然有规律:
当n是偶数的时候,最小操作数是1 + 3 + 5 + ... + n-1 = n*n/4
当n是奇数的时候,最小操作数是2 + 4 + ... + n-1 = (n*n - 1) / 4

注意: 上面的求和公式都是数学形式

那能不能再简单一点呢? 如果用整除代替数学中的除法,可以将(n*n - 1) / 4修改成n*n/4,因为1整除4为0不影响最后的结果.
所以有了下面的代码,是不是很酷 :)

 int minOperations(int n) {
     reutrn n * n / 4;
}
// 时空复杂度都是 O(1)

 



posted @ 2020-08-17 12:52  屁屁-rtq  阅读(530)  评论(0编辑  收藏  举报