题目为 给定一个整数数组 nums,按要求返回一个新数组 counts。数组 counts 有该性质: counts[i] 的值是 nums[i] 右侧小于 nums[i] 的元素的数量。
做这题前,首先应掌握归并排序归并排序介绍以及代码,然后做一下数组中的逆序对这道题,之后做这道题会理解的很快。
思路如下,归并排序,操作数组排序同时,新建一个数组记录元素位置,对元素排序时同时对该位置数组同样操作,这样可以记录元素位置,然后比较大小,直接根据位置,向结果数组中添加数量就可以了。
一开始的代码如下,第一次写的代码超时了,主要费时在比较后向左侧有序数组的所有元素大于右侧数量都加一的循环中。如下代码
for (int w = i; w <= middle; w++)
{
resAllCountSmaller[Pos[w]] += 1;
}
整体代码如下
public IList<int> CountSmaller(int[] nums)
{
MergeSort(nums);
return resAllCountSmaller.ToList();
}
int[] resAllCountSmaller = null;
public void MergeSort(int[] nums)
{
int allCount = nums.Count();
resAllCountSmaller = new int[allCount];
int[] temp = new int[allCount];
int[] Pos = new int[allCount];
for (int i = 0; i < allCount; i++)
{
Pos[i] = i;
}
mergeSortSec(nums, 0, allCount - 1, temp, Pos);
}
public void mergeSortSec(int[] nums,int left,int right,int[] temp,int[] Pos)
{
if (left<right)
{
int middle = (right + left) / 2;
mergeSortSec(nums, left, middle, temp, Pos);
mergeSortSec(nums, middle+1, right, temp, Pos);
SortHalf(nums, left, middle, right, temp, Pos , new int[temp.Count()]);
}
}
public void SortHalf(int[] nums, int left, int middle,int right, int[] temp, int[] Pos,int[] posTemp)
{
int i = left, j = middle + 1,pos=0;
while (i <= middle&& j <= right)
{
if (nums[i] <= nums[j])
{
posTemp[pos] = Pos[i];
temp[pos++] = nums[i++];
}
else
{
for (int w = i; w <= middle; w++)
{
resAllCountSmaller[Pos[w]] += 1;
}
posTemp[pos] = Pos[j];
temp[pos++] = nums[j++];
}
}
while (i <= middle)
{
posTemp[pos] = Pos[i];
temp[pos++] = nums[i++];
}
while (j <= right)
{
posTemp[pos] = Pos[j];
temp[pos++] = nums[j++];
}
pos = 0;
while (left<=right)
{
Pos[left] = posTemp[pos];
nums[left++] = temp[pos++];
}
}
看了官方题解后,发现不用去把时间用在向左侧有序数组的所有元素大于右侧数量都加一的循环中。直接用右侧的比较完成元素减去中间值,代表已经添加的小于左侧的元素值就可以了。
ans[index[i]] += (j - mid - 1);
官方题解代码如下
public IList<int> CountSmaller(int[] nums)
{
this.index = new int[nums.Length];
this.temp = new int[nums.Length];
this.tempIndex = new int[nums.Length];
this.ans = new int[nums.Length];
for (int i = 0; i < nums.Length; ++i)
{
index[i] = i;
}
int l = 0, r = nums.Length - 1;
MergeSort(nums, l, r);
return new List<int>(ans);
}
private int[] index;
private int[] temp;
private int[] tempIndex;
private int[] ans;
public void MergeSort(int[] a, int l, int r)
{
if (l >= r)
{
return;
}
int mid = (l + r) >> 1;
MergeSort(a, l, mid);
MergeSort(a, mid + 1, r);
Merge(a, l, mid, r);
}
public void Merge(int[] a, int l, int mid, int r)
{
int i = l, j = mid + 1, p = l;
while (i <= mid && j <= r)
{
if (a[i] <= a[j])
{
temp[p] = a[i];
tempIndex[p] = index[i];
ans[index[i]] += (j - mid - 1);
++i;
++p;
}
else
{
temp[p] = a[j];
tempIndex[p] = index[j];
++j;
++p;
}
}
while (i <= mid)
{
temp[p] = a[i];
tempIndex[p] = index[i];
ans[index[i]] += (j - mid - 1);
++i;
++p;
}
while (j <= r)
{
temp[p] = a[j];
tempIndex[p] = index[j];
++j;
++p;
}
for (int k = l; k <= r; ++k)
{
index[k] = tempIndex[k];
a[k] = temp[k];
}
}
根据官方题解,将我的代码优化如下,
将所有传入数组参数全部去除,改为全局变量方式更改。
计算大于右侧数的方式为右侧有序数组已经放入新数组中的个数。
代码如下
public IList<int> CountSmaller(int[] nums)
{
MergeSort(nums);
return new List<int>(resAllCountSmaller);//.ToList();
}
int[] resAllCountSmaller = null;
int[] temp = null;
int[] Pos = null;
int[] posTemp = null;
public void MergeSort(int[] nums)
{
int allCount = nums.Count();
resAllCountSmaller = new int[allCount];
temp = new int[allCount];
Pos = new int[allCount];
posTemp = new int[allCount];
for (int i = 0; i < allCount; i++)
{
Pos[i] = i;
}
mergeSortSec(nums, 0, allCount - 1);
}
public void mergeSortSec(int[] nums, int left, int right)
{
if (left < right)
{
int middle = (right + left) / 2;
mergeSortSec(nums, left, middle);
mergeSortSec(nums, middle + 1, right);
SortHalf(nums, left, middle, right);
}
}
public void SortHalf(int[] nums, int left, int middle, int right)
{
int i = left, j = middle + 1, pos = 0;
while (i <= middle && j <= right)
{
if (nums[i] <= nums[j])
{
resAllCountSmaller[Pos[i]] += j - middle - 1;
posTemp[pos] = Pos[i];
temp[pos++] = nums[i++];
}
else
{
posTemp[pos] = Pos[j];
temp[pos++] = nums[j++];
}
}
while (i <= middle)
{
resAllCountSmaller[Pos[i]] += j - middle - 1;
posTemp[pos] = Pos[i];
temp[pos++] = nums[i++];
}
while (j <= right)
{
posTemp[pos] = Pos[j];
temp[pos++] = nums[j++];
}
pos = 0;
while (left <= right)
{
Pos[left] = posTemp[pos];
nums[left++] = temp[pos++];
}
}