法1:归并排序—构造索引类
这种写法更容易理解。
class Solution {
List<Integer> ans = new ArrayList<>();
int[] count; // 记录题目中所求的count[i]
Pair[] tmp; // 临时数组, 存储一次归并过程中排序好的【两段有序的元素】
public List<Integer> countSmaller(int[] nums) {
int n = nums.length;
Pair[] pairArray = new Pair[n];
count = new int[n];
tmp = new Pair[n];
for (int i = 0; i < n; i++) {
pairArray[i] = new Pair(nums[i], i);
}
mergeSort(pairArray, 0, n - 1);
for (int i = 0; i < n; i++) {
ans.add(count[i]);
}
return ans;
}
private void mergeSort(Pair[] pairArray, int start, int end) {
if (start < end) {
int mid = start + (end - start) / 2;
mergeSort(pairArray, start, mid);
mergeSort(pairArray, mid + 1, end);
merge(pairArray, start, mid, end); // 再将两个有序数组合并,只不过这里要承接统计count[i]的功能
}
}
// 注意: 降序merge!!!
private void merge(Pair[] pairArray, int start, int mid, int end) {
int left = start;
int right = mid + 1;
int cur = left;
for (int i = start; i <= end; ++i) {
tmp[i] = pairArray[i];
}
while (left <= mid || right <= end) {
if (left > mid) { // 这种情况无需再重复赋值, 直接break
break;
} else if (right > end) {
pairArray[cur] = tmp[left];
++left;
} else if (tmp[left].val > tmp[right].val) {
count[tmp[left].idx] += end - right + 1; // 右半部分tmp[right, end]都 < nums[left]
pairArray[cur] = tmp[left];
++left;
} else { // tmp[left].val <= tmp[right].val
pairArray[cur] = tmp[right];
++right;
}
++cur;
}
}
class Pair {
public int val;
public int idx;
public Pair(int val, int idx) {
this.val = val;
this.idx = idx;
}
}
}
法2:归并排序—索引数组
必须掌握的基础算法!!!
class Solution {
List<Integer> ans = new ArrayList<>();
int[] index; // 原数组的索引数组,存储着原数组中每个元素对应的下标
int[] count; // 记录题目中所求的count[i]
int[] tmp; // 临时数组, 存储一次归并过程中排序好的元素
int[] tmpIndex; // 临时索引数组, 存储一次归并过程中排序好的元素对应原始索引
//入口
public List<Integer> countSmaller(int[] nums) {
int n = nums.length;
index = new int[n];
count = new int[n];
tmp = new int[n];
tmpIndex = new int[n];
for (int i = 0; i < n; i++) {
index[i] = i;
}
mergeSort(nums, 0, n - 1);
for (int i = 0; i < n; i++) {
ans.add(count[i]);
}
return ans;
}
private void mergeSort(int[] nums, int start, int end) {
if (start < end) {
int mid = start + (end - start) / 2;
mergeSort(nums, start, mid);
mergeSort(nums, mid + 1, end);
merge(nums, start, mid, end); // 再将两个有序数组合并,只不过这里要承接统计count[i]的功能
}
}
// 注意: 降序merge!!!
private void merge(int[] nums, int start, int mid, int end) {
int left = start;
int right = mid + 1;
int cur = left;
while (left <= mid && right <= end) {
if (nums[left] > nums[right]) {
count[index[left]] += end - right + 1; // 右半部分小于nums[left]元素的数目
tmpIndex[cur] = index[left]; // 记录元素位置的改变
tmp[cur] = nums[left];
++left;
} else {
tmp[cur] = nums[right];
tmpIndex[cur] = index[right];
++right;
}
++cur;
}
while (left <= mid) {
tmp[cur] = nums[left];
tmpIndex[cur] = index[left];
++left;
++cur;
}
while (right <= end) {
tmp[cur] = nums[right];
tmpIndex[cur] = index[right];
++right;
++cur;
}
for (int i = start; i <= end; i++) {
nums[i] = tmp[i];
index[i] = tmpIndex[i];
}
}
}