1. 排序数组(912)
题目描述:
算法原理:
这题使用归并排序的方法可以直接解决,至于归并排序的原理就是不断地去将数组一分为二,之后将分隔地不同层次的数组两两合一。
代码如下:
class Solution {
public int[] sortArray(int[] nums) {
mergeSort(nums, 0, nums.length - 1);
return nums;
}
private void mergeSort(int[] nums, int left, int right) {
if (left >= right) {
return;
}
int mid = (left + right) / 2;
mergeSort(nums, left, mid);
mergeSort(nums, mid + 1, right);
int cur1 = left, cur2 = mid + 1;
int[] temp = new int[right - left + 1];
int i = 0;
while (cur1 <= mid && cur2 <= right) {
temp[i++] = (nums[cur1] <= nums[cur2]) ? nums[cur1++] : nums[cur2++];
}
while (cur1 <= mid) {
temp[i++] = nums[cur1++];
}
while (cur2 <= right) {
temp[i++] = nums[cur2++];
}
for (int j = left; j <= right; j++) {
nums[j] = temp[j - left];
}
}
}
2. 交易逆序对的总数(LCR 170)
题目描述:
算法原理:
这一题要我们去求数组中的逆序对的数量,假设我们将数组分为两部分,我们可以分别求出两部分中的逆序对数量之后再去求两个部分之间交互的逆序对数量。不难发现这个过程在宏观上是与归并排序类似的,归并排序也会将数组分为两个部分,我们可以先求出这两个部分的逆序对数量之后再去利用最后合并两个数组的过程来求得最后的两个部分交互的逆序对数量。
代码如下:
class Solution {
public int reversePairs(int[] record) {
int n = record.length;
return mergeSort(record, 0, n - 1);
}
public int mergeSort(int[] arr, int left, int right) {
if (left >= right) {
return 0;
}
int ret = 0;
int mid = (right + left) / 2;
ret += mergeSort(arr, left, mid);
ret += mergeSort(arr, mid + 1, right);
int cur1 = left, cur2 = mid + 1, i = 0;
int temp[] = new int[right - left + 1];
while (cur1 <= mid && cur2 <= right) {
if (arr[cur1] > arr[cur2]) {
ret += (right - cur2 + 1);
temp[i++] = arr[cur1++];
} else {
temp[i++] = arr[cur2++];
}
}
while (cur1 <= mid) {
temp[i++] = arr[cur1++];
}
while (cur2 <= right) {
temp[i++] = arr[cur2++];
}
for (int j = left; j <= right; j++) {
arr[j] = temp[j - left];
}
return ret;
}
}
3. 计算右侧小于当前元素的个数(315)
题目描述:
算法原理:
这一题要求去求数组nums中每个元素的右侧小于该元素的个数,实际上就是求元素逆序对的个数,只不过要将对应个数保存到相应数组,做法和上一题是类似的,只不过因为最终是要经过归并排序的,数组nums中元素顺序要被打乱,所以需要建立一个数组来保存nums数组中的元素与下标之间的对应关系。
代码如下:
class Solution {
int n;
int[] ret;
int[] index;
int[] temp;
int[] tempIndex;
public List<Integer> countSmaller(int[] nums) {
n = nums.length;
ret = new int[n];
index = new int[n];
temp = new int[n];
tempIndex = new int[n];
for (int i = 0; i < n; i++) {
index[i] = i;
}
mergeSort(nums, 0, n - 1);
List<Integer> list = new ArrayList<>();
for (int x : ret) {
list.add(x);
}
return list;
}
public void mergeSort(int[] nums, int left, int right) {
if (left >= right) {
return;
}
int mid = (left + right) / 2;
mergeSort(nums, left, mid);
mergeSort(nums, mid + 1, right);
int cur1 = left, cur2 = mid + 1;
int i = 0;
while (cur1 <= mid && cur2 <= right) {
if (nums[cur1] > nums[cur2]) {
ret[index[cur1]] += (right - cur2 + 1);
temp[i] = nums[cur1];
tempIndex[i++] = index[cur1++];
} else {
temp[i] = nums[cur2];
tempIndex[i++] = index[cur2++];
}
}
while (cur1 <= mid) {
temp[i] = nums[cur1];
tempIndex[i++] = index[cur1++];
}
while (cur2 <= right) {
temp[i] = nums[cur2];
tempIndex[i++] = index[cur2++];
}
for (int j = left; j <= right; j++) {
nums[j] = temp[j - left];
index[j] = tempIndex[j - left];
}
}
}
4. 翻转对(493)
题目描述:
算法原理:
这里与前面两题的区别是求得逆序对需要满足大于两倍这种条件,因此无法与归并的过程结合,需要先计算出逆序对的数量之后再进行归并。
代码如下:
class Solution {
int[] temp;
public int reversePairs(int[] nums) {
int n = nums.length;
temp = new int[n];
return mergeSort(nums, 0, n - 1);
}
public int mergeSort(int[] nums, int left, int right) {
if (left >= right) {
return 0;
}
int ret = 0;
int mid = (right + left) / 2;
ret += mergeSort(nums, left, mid);
ret += mergeSort(nums, mid + 1, right);
int cur1 = left, cur2 = mid + 1, i = 0;
while (cur2 <= right) {
while (cur1 <= mid && nums[cur1] / 2.0 <= nums[cur2]) {
cur1++;
}
if (cur1 > mid) {
break;
}
ret += (mid-cur1 + 1);
cur2++;
}
cur1 = left;
cur2 = mid + 1;
while (cur1 <= mid && cur2 <= right) {
if (nums[cur1] <= nums[cur2]) {
temp[i++] = nums[cur1++];
} else {
temp[i++] = nums[cur2++];
}
}
while (cur1 <= mid) {
temp[i++] = nums[cur1++];
}
while (cur2 <= right) {
temp[i++] = nums[cur2++];
}
for (int j = left; j <= right; j++) {
nums[j] = temp[j - left];
}
return ret;
}
}