Minimum Moves to Equal Array Elements

Leetcode-Algorithm-Math453

题目:
Given a non-empty integer array of size n, find the minimum number of moves required to make all array elements equal, where a move is incrementing n - 1 elements by 1.
(给定一个大小为n的非空整型数组,若每一步都需要让n-1个元素自增1,请问,需要多少步才能让数组中所有的元素都相等?)

例子:
input:
[1,2,3]

output:
3

Explanation:
Only three moves are needed (remember each move increments two elements):
[1,2,3]->[2,3,3]->[3,4,3]->[4,4,4]


题解:

方法1:(刚开始用的方法)
由题意可知,若每次使n-1个元素自增1,那么在每次迭代中最大的元素max是不变的,因此在每次迭代中需要找到最大的元素,然后把其它元素自增1。通过思考可以知道,至少需要最大元素max和最小元素min之差次迭代,因此可以通过max-min来判断数组中的元素是否已经全部相等,最后返回迭代次数。

    int minMoves(vector<int>& nums) {
        bool isChange = false;
        long moves = 0;

        do {
            int maxIndex = 0;
            isChange = false;

            //除最大数以外其它元素自增
            for (vector<int>::size_type ix = 1; ix < nums.size(); ++ix) {
                if (nums[ix] < nums[maxIndex]) {
                    ++nums[ix];
                    isChange = true;
                }
                else if (nums[ix] == nums[maxIndex])
                    ++nums[ix];
                else {
                    ++nums[maxIndex];
                    maxIndex = ix;
                    isChange = true;
                }
            }

            //nums有所变化,说明元素不全相等,moves自增
            if (isChange)
                ++moves;

        } while (isChange);

        return moves;
    }

不足:
该方法会产生Time Limit Exceeded的问题。因为该方法需要两层循环,外层循环用于迭代(层循环每次迭代都会遍历一次数组元素,所以时间复杂度为 O(n2) 。当输入n非常大时,将需要很长的时间才能得到输出。


方法2:
由方法1知,每次都要找到最大的元素max和最小的元素min,那么我们可以先将数组变成有序数组,那么每次迭代的max和min都在数组的两端。而且,在每次迭代中,我们通过max-min次迭代使得min和max相等,其它元素也相应增加max-min,这样使得前n-1个元素仍然构成一个有序数组,而且第一个元素仍然是最小的元素min’,而最大的元素现在是第n-1个元素max’,所以重复以上步骤直到迭代到第一个元素,这样问题就简化了。此外,在这里的每次迭代中,尽管前n-i个元素都增加了相应的max-min次,但是它们之间的差值是不变的,而我们要计算的步数moves正好是每次迭代中,max和min的差值,这样问题又再一次简化。最后只需要做一次排序,然后把其它元素和第一个元素之间的差值加起来就能得到我们需要的moves了。

int minMoves(vector<int>& nums) {
    if (nums.empty()) return 0;
    sort(nums.begin(), nums.end());
    int diff = 0, i = nums.size()-1;
    while (i >= 0) {
        diff += nums[i] - nums[0];
        --i;
    }
    return diff;
}

分析:
该方法只需要一个循环,主要的开销在于对数组进行排序,因此时间复杂度为 O(n2)


方法3:
该方法是一个数学方法。通过分析我们可以知道,数组中最小的元素在整个计算过程中都需要自增直到整个数组都相等的。那么我们设开始迭代前数组元素总和为sum,最小的元素为min,在m步迭代后得到的所有数组元素为x。
首先,通过题意我们有

sum+m(n1)=xn
然后,由于最小的元素一直在自增,我们有
x=min+m
所以,联立以上两个式子,我们可以得到
m=summinn
m就是我们想要的步骤数。

int minMoves(vector<int>& nums) {
    int sum = 0, minIndex = 0;
    for (int i = 0; i < nums.size()-1; ++i) {
        sum += nums[i];
        if (nums[i] < nums[minIndex])
            minIndex = i;
    }
    return sum-nums[minIndex]*nums.size();
}

分析:
该方法只是用到基本的数学步骤,主要开销在于计算数组元素之和,因此时间复杂度为 O(n2)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值