题目为 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
这道题可以当作例题做,思路为归并排序。不理解什么是归并排序可以看我的文章 排序算法之插入排序、希尔排序、归并排序(C#)。当两个有序数组进行合并时,左侧有比右侧大的元素,则左侧该元素之后所有之后的元素均为逆序对。一开始我的代码为
public class Solution {
public int ReversePairs(int[] nums) {
if (nums.Count()==0||nums.Count()==1)
{
return 0;
}
ReversePairsSec(nums);
return Reverseres;
}
public int[] ReversePairsSec(int[] nums)
{
if (nums.Count()==1)
{
return nums;
}
int count = nums.Count();
int half = count / 2;
int[] left = nums.Take(half).ToArray();
int[] right = nums.Skip(half).ToArray();
int[] leftNew= ReversePairsSec(left);
int[] rightNew= ReversePairsSec(right);
return MergeSortPairs(leftNew, rightNew);
}
int Reverseres = 0;
public int[] MergeSortPairs(int[] numsLeft,int[] numsRight)
{
int leftPos = 0, rightPos = 0;
List<int> resTemp = new List<int>();
while (leftPos<numsLeft.Count()&&rightPos<numsRight.Count())
{
if (numsLeft[leftPos]>numsRight[rightPos])
{
Reverseres += (numsLeft.Count() - leftPos);
resTemp.Add(numsRight[rightPos]);
rightPos++;
}
else
{
resTemp.Add(numsLeft[leftPos]);
leftPos++;
}
}
if (rightPos < numsRight.Count())
{
resTemp.AddRange(numsRight.Skip(rightPos));
}
if (leftPos < numsLeft.Count())
{
resTemp.AddRange(numsLeft.Skip(leftPos));
}
return resTemp.ToArray();
}
}
效率不高,参照其他题解更改后,优化代码为
public class Solution {
public int ReversePairs(int[] nums)
{
if (nums == null || nums.Length < 2)
{
return 0;
}
var copy = new int[nums.Length];
Array.Copy(nums, copy, nums.Length);
var tmpArr = new int[nums.Length];
return ReversePairs(copy, 0, nums.Length - 1, tmpArr);
}
private int ReversePairs(int[] nums, int left, int right, int[] tmpArr)
{
if (left == right)
{
return 0;
}
int middle = left + ((right - left) >> 1);
int leftCount = ReversePairs(nums, left, middle, tmpArr); // 归
int rightCount = ReversePairs(nums, middle + 1, right, tmpArr);// 归
int crossCount = MergeAndCount(nums, left, middle, right, tmpArr); // 并
return leftCount + rightCount + crossCount;
}
private int MergeAndCount(int[] nums, int left, int middle, int right, int[] tmpArr)
{
// 重复使用tmpArr, 减少每次重建数组和销毁数组的开销
for (int i = left; i <= right; i++)
{
tmpArr[i] = nums[i];
}
int count = 0;
int leftIndex = left;
int rightIndex = middle + 1;
int tmpArrIndex = left;
while (leftIndex <= middle && rightIndex <= right)
{
if (tmpArr[leftIndex] <= tmpArr[rightIndex])
{
nums[tmpArrIndex++] = tmpArr[leftIndex++];
}
else
{
nums[tmpArrIndex++] = tmpArr[rightIndex++];
count += middle - leftIndex + 1;
}
}
if (leftIndex <= middle)
{
for (int i = leftIndex; i <= middle; i++)
{
nums[tmpArrIndex++] = tmpArr[i];
}
}
if (rightIndex <= right)
{
for (int i = rightIndex; i <= right; i++)
{
nums[tmpArrIndex++] = tmpArr[i];
}
}
return count;
}
}
对比了一下原因,因为第一个的代码有很多的ToArray()使用,占用时间。另外就是每次遍历都要新建数组,占用时间和空间。而第二版代码就节省了很多,都在Nums上操作和一个临时数组操作。节省空间。且没有数组转换。