Sword51——数组中的逆序对
方法1——归并排序
- 思路:归并排序思想,将数组进行划分为左右子数组,并且保证子数组其中是有序的
- 假设只剩下最后两组元素left和right时,当left[i]大于right[j]时,又因为left为有序的,因此left数组后续元素都将大于right[j],将其填入nums原数组,并统计新增的逆序对个数
- 此处并没有真正将数组划分左右子数组,只是使用一个tmp进行存储,再定义左右子数组的首个元素的起始坐标,以达到划分的目的
- 特殊情况与临界分析:无
- 终止条件:无
- 步骤:
- 定义全局nums数组、tmp临时数组(模拟左右子数组的划分)
- 初始化nums
- 初始化全局变量nums、tmp
- 进行归并
- 归并方法(划分 + 合并)
- 参数:原数组(已为全局变量可不传递)、起始坐标、终止坐标
- 终止条件:当start大于end时,此时子数组长度为1停止划分,返回新增逆序对为0
- 获取中间元素下标
- 递归划分,并统计结果(等于其左半数组与mid的结果、加上其右半数组与mid的结果)
- 合并阶段,记录当前左右子数组的首部元素(start、及mid + 1)
- for循环将nums元素复制到tmp中,从start到end
- 通过tmp数组,加上左右子数组的首部元素下标,达到划分数组的目的
- for循环从start开始,直到end
- 主要分为四种情况
- 1)当左指针left已到mid + 1时,左侧数组已经合并完成,此时不会再出现逆序对
- 将tmp的右指针right对应元素填入nums当前下标,并且j后移
- 2)3)当right为end+ 1(右侧数组已经合并完成)、或tmp中left位置比right位置要小
- 将tmp的左指针left对应元素填入nums当前下标,并且i后移
- 4)否则(即tmp中left位置比right位置要大)
- 将tmp的右指针right对应元素填入nums当前下标,并且j后移
- 只有此时才能统计逆序对,新增逆序对个数为mid - 左指针left + 1
- 返回结果res
int[] nums, tmp;
public int reversePairs(int[] nums) {
this.nums = nums;
tmp = new int[nums.length];
return mergeSort(0, nums.length - 1);
}
private int mergeSort(int start, int end) {
if (start >= end) {
return 0;
}
int mid = start + (end - start)/2;
int res = mergeSort(start, mid) + mergeSort(mid + 1, end);
int left = start, right = mid + 1;
for (int i = start; i <= end; i++) {
tmp[i] = nums[i];
}
for (int i = start; i <= end; i++) {
if (left == mid + 1) {
nums[i] = tmp[right++];
} else if (right == end + 1 || tmp[left] <= tmp[right]) {
nums[i] = tmp[left++];
} else {
nums[i] = tmp[right++];
res += mid + 1 - left;
}
}
return res;
}
