博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[LeetCode] Minimum Moves to Equal Array Elements II 最少移动次数使数组元素相等之二
阅读量:5745 次
发布时间:2019-06-18

本文共 2987 字,大约阅读时间需要 9 分钟。

 

Given a non-empty integer array, find the minimum number of moves required to make all array elements equal, where a move is incrementing a selected element by 1 or decrementing a selected element by 1.

You may assume the array's length is at most 10,000.

Example:

Input:[1,2,3]Output:2Explanation:Only two moves are needed (remember each move increments or decrements one element):[1,2,3]  =>  [2,2,3]  =>  [2,2,2]

 

这道题是之前那道的拓展,现在我们可以每次对任意一个数字加1或者减1,让我们用最少的次数让数组所有值相等。一般来说这种题目是不能用暴力方法算出所有情况,因为OJ一般是不会答应的。那么这道题是否像上面一道题一样,有巧妙的方法呢?答案是肯定的。下面这种解法实际上利用了之前一道题的思想,是不感觉很amazing,看似完全不相干的两道题,居然有着某种内部联系。我们首先给数组排序,那么我们最终需要变成的相等的数字就是中间的数,如果数组有奇数个,那么就是最中间的那个数字;如果是偶数个,那么就是中间两个数的区间中的任意一个数字。而两端的数字变成中间的一个数字需要的步数实际上就是两端数字的距离,讲到这里发现是不是就和这道题的思路是一样了。那么我们就两对两对的累加它们的差值就可以了,参见代码如下:

 

解法一:

class Solution {public:    int minMoves2(vector
& nums) { int res = 0, i = 0, j = (int)nums.size() - 1; sort(nums.begin(), nums.end()); while (i < j) { res += nums[j--] - nums[i++]; } return res; }};

 

既然有了上面的分析,我们知道实际上最后相等的数字就是数组的最中间的那个数字,那么我们在给数组排序后,直接利用坐标定位到中间的数字,然后算数组中每个数组与其的差的绝对值累加即可,参见代码如下:

 

解法二:

class Solution {public:    int minMoves2(vector
& nums) { sort(nums.begin(), nums.end()); int res = 0, mid = nums[nums.size() / 2]; for (int num : nums) { res += abs(num - mid); } return res; }};

 

上面的两种方法都给整个数组排序了,时间复杂度是O(nlgn),其实我们并不需要给所有的数字排序,我们只关系最中间的数字,那么这个stl中自带的函数nth_element就可以完美的发挥其作用了,我们只要给出我们想要数字的位置,它就能在O(n)的时间内返回正确的数字,然后算数组中每个数组与其的差的绝对值累加即可,参见代码如下:

 

解法三:

class Solution {public:    int minMoves2(vector
& nums) { int res = 0, n = nums.size(), mid = n / 2; nth_element(nums.begin(), nums.begin() + mid, nums.end()); for (int i = 0; i < n; ++i) { res += abs(nums[i] - nums[mid]); } return res; }};

 

下面这种方法是改进版的暴力破解法,它遍历了所有的数字,让每个数字都当作最后相等的值,然后算法出来总步数,每次和res比较,留下较小的。而这种方法叼就叼在它在O(1)的时间内完成了步数统计,那么这样整个遍历下来也只是O(n)的时间,不过由于还是要给数组排序,所以整体的时间复杂度是O(nlgn),这已经能保证可以通过OJ啦。那么我们来看看如何快速计算总步数,首先我们给数组排序,我们假设中间某个位置有个数字k,那么此时数组就是:nums[0], nums[1], ..., k, ..., nums[n - 1], 如果i为数字k在数组中的坐标,那么有k = nums[i],那么总步数为:

Y = k - nums[0] + k - nums[1] + ... + k - nums[i - 1] + nums[i] - k + nums[i + 1] - k + ... + nums[n - 1] - k

   = i * k - (nums[0] + nums[1] + ... + nums[i - 1]) + (nums[i] + nums[i + 1] + ... + nums[n - 1]) - (n - i) * k

   = 2 * i * k - n * k + sum - 2 * curSum

那么我们只要算出sum和curSum就可以快速得到总步数了,数组之和可以通过遍历数组计算出来,curSum可以在遍历的过程中累加,那么我们就可以算出总步数,然后每次更新结果res了,参见代码如下:

 

解法四:

class Solution {public:    int minMoves2(vector
& nums) { sort(nums.begin(), nums.end()); long long sum = accumulate(nums.begin(), nums.end(), 0); long long res = LONG_MAX, curSum = 0; int n = nums.size(); for (int i = 0; i < n; ++i) { long long k = nums[i]; curSum += k; res = min(res, 2 * k * (i + 1) - n * k + sum - 2 * curSum); } return res; }};

 

类似题目:

 

参考资料:

 

转载地址:http://pxxzx.baihongyu.com/

你可能感兴趣的文章
搜索问题的办法
查看>>
微信分销系统商城营销5大重点
查看>>
求职准备 - 收藏集 - 掘金
查看>>
htm5新特性(转)
查看>>
Linux-Centos启动流程
查看>>
php 设计模式
查看>>
后端技术精选 - 收藏集 - 掘金
查看>>
Laravel 服务容器
查看>>
mac安装kubernetes并运行echoserver
查看>>
多页架构的前后端分离方案(webpack+express)
查看>>
算法(第4版) Chapter 1
查看>>
前端技术选型的遗憾和经验教训
查看>>
“亲切照料”下的领域驱动设计
查看>>
SRE工程师到底是做什么的?
查看>>
解读:Red Hat为什么收购Ansible
查看>>
Ossim下的安全合规管理
查看>>
DelphiWebMVC框架下BPL热部署实现
查看>>
C++与MySQL的冲突
查看>>
siki学习之观察者模式笔记
查看>>
单元测试
查看>>