在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例 1:
输入: [7,5,6,4]
输出: 5
其中逆序对分别是:
[7,5]
[6,4]
[5,4]
[7,4]
[7,6]
解决思路一:
暴力求解O(n^2)的时间复杂度,O(1)的空间复杂度,并且暴力求解是不会改变原有数组的顺序的,leetcode上面提交会超时:
class Solution {
public:
int reversePairs(vector<int>& nums) {
int count = 0;
for(int i = 0; i < nums.size() - 1; ++i)
{
for(int j = i + 1; j < nums.size(); ++j)
{
if(nums[i] > nums[j]) ++count;
}
}
return count;
}
}
解决思路二:
采用分而治之归并排序的思想:
在对数组中的元素遍历处理时,比较的时候不和它后面的所有元素进行比较,而是比较和它相邻的元素,采用分而治之的思想,将一个大的数组,划分为最小的单元,即元素个数为1的数组,然后,再进行反向进行归并排序,在归并排序的过程中统计逆序对的数量。
比如数组:[7,5,6,4]
当将数组分割到最小的单元:[7] [5] [6] [4]
然后进行Merge时:
[7] > [5] count = 1; 归并结果[5,7]
[6] > [4] count = 2; 归并结果[4,6]
再次进行Merge:
[5,7] [4,6]
因为5大于4,,所以左边都大于右边,记录有[5,4] [7,4],count = 4;
遍历到7时,因为7 > 6,所以[7, 6],count = 5;
时间复杂度O(N*lgN),空间复杂度O(N),改变了数组原有顺序。
class Solution {
public:
int reversePairs(vector<int>& nums) {
return MergeSort(nums, 0, nums.size()-1);
}
int MergeSort(vector<int>& nums, int left, int right)
{
int count = 0;
if (left >= right)
{
return count;
}
int mid = left + ((right - left) >> 2);
count += MergeSort(nums, left, mid); //左半部分的逆序数
count += MergeSort(nums, mid + 1, right); //右半部分的逆序数
if (nums[mid] <= nums[mid + 1])
{
return count; //如果此时左半部分的最大值已经小于右半部分的最小值,那么就不需要执行merge过程,也不会产生逆序对
}
count += Merge(nums, left, mid, right); //merge过程产生的逆序数
return count;
}
int Merge(vector<int>& nums, int left, int mid, int right)
{
int idx = 0;
int count = 0;
int leftIndex = left;
int rightIndex = mid + 1;
vector<int> tmp;
tmp.reserve(right - left + 1);
if (left >= right)
{
return count;
}
while (leftIndex <= mid && rightIndex <= right)
{
if (nums[leftIndex] > nums[rightIndex])
{
tmp[idx++] = nums[rightIndex++];
count += mid - leftIndex + 1;
}
else
{
tmp[idx++] = nums[leftIndex++];
}
}
while (leftIndex <= mid)
{
tmp[idx++] = nums[leftIndex++];
}
while (rightIndex <= right)
{
tmp[idx++] = nums[rightIndex++];
}
for (int i = 0; i < idx; i++)
{
nums[left + i] = tmp[i];
}
return count;
}
};