归并排序
归并排序:
先分治后归并。
//对于归并时的临时存储数组,可以全局使用一个,而不必每次归并都重新申请 /* Java program for Merge Sort */ class MergeSort { // Merges two subarrays of arr[]. // First subarray is arr[l..m] // Second subarray is arr[m+1..r] void merge(int arr[], int l, int m, int r) { // Find sizes of two subarrays to be merged int n1 = m - l + 1; int n2 = r - m; /* Create temp arrays */
int L[] = Arrays.copyOfRange(arr, l, m + 1);
int R[] = Arrays.copyOfRange(arr, m+1, r+1);
/* Merge the temp arrays */ // Initial indexes of first and second subarrays int i = 0, j = 0; // Initial index of merged subarry array int k = l; while (i < n1 && j < n2) { if (L[i] <= R[j]) { arr[k] = L[i]; i++; } else { arr[k] = R[j]; j++; } k++; } /* Copy remaining elements of L[] if any */ while (i < n1) { arr[k] = L[i]; i++; k++; } /* Copy remaining elements of R[] if any */ while (j < n2) { arr[k] = R[j]; j++; k++; } } // Main function that sorts arr[l..r] using // merge() void sort(int arr[], int l, int r) { if (l < r) { // Find the middle point int m = (l + r) / 2; // Sort first and second halves sort(arr, l, m); sort(arr, m + 1, r); // Merge the sorted halves merge(arr, l, m, r); } }
给定一个整数数组 nums,按要求返回一个新数组 counts。数组 counts 有该性质: counts[i]
的值是 nums[i]
右侧小于 nums[i]
的元素的数量。
示例:
输入: [5,2,6,1]
输出: [2,1,1,0]
解释:
5 的右侧有 2 个更小的元素 (2 和 1).
2 的右侧仅有 1 个更小的元素 (1).
6 的右侧有 1 个更小的元素 (1).
1 的右侧有 0 个更小的元素.
import java.util.ArrayList; import java.util.List; class Solution { private int[] temp; private int[] counter; private int[] indexes; public List<Integer> countSmaller(int[] nums) { List<Integer> res = new ArrayList<>(); int len = nums.length; if (len == 0) { return res; } temp = new int[len]; //merge临时数组 counter = new int[len]; //目标数组 indexes = new int[len]; //索引数组 for (int i = 0; i < len; i++) { indexes[i] = i; } mergeAndCountSmaller(nums, 0, len - 1); for (int i = 0; i < len; i++) { res.add(counter[i]); } return res; } /** * 针对数组 nums 指定的区间 [l, r] 进行归并排序,在排序的过程中完成统计任务 * * @param nums //原数组不变 * @param l //inclusive * @param r //inclusive */ private void mergeAndCountSmaller(int[] nums, int l, int r) { if (l == r) { // 数组只有一个元素的时候,没有比较,不统计 return; } int mid = (l + r - l) / 2; mergeAndCountSmaller(nums, l, mid); mergeAndCountSmaller(nums, mid + 1, r); // 归并排序的优化,同样适用于该问题 // 如果索引数组有序,就没有必要再继续计算了 if (nums[indexes[mid]] > nums[indexes[mid + 1]]) { mergeOfTwoSortedArrAndCountSmaller(nums, l, mid, r); } } /** * 归并过程 * [l, mid] 是排好序的 * [mid + 1, r] 是排好序的 * * @param nums * @param l * @param mid * @param r */ private void mergeOfTwoSortedArrAndCountSmaller(int[] nums, int l, int mid, int r) { // 3,4 1,2 for (int i = l; i <= r; i++) { temp[i] = indexes[i]; } int i = l; int j = mid + 1; // 左边出列的时候,计数 for (int k = l; k <= r; k++) { if (i > mid) { indexes[k] = temp[j]; j++; } else if (j > r) { indexes[k] = temp[i]; i++; // 此时 j 用完了,[7,8,9 | 1,2,3] // 之前的数就和后面的区间长度构成逆序 counter[indexes[k]] += (r - mid); } else if (nums[temp[i]] <= nums[temp[j]]) { indexes[k] = temp[i]; i++; // 此时 [4,5, 6 | 1,2,3 10 12 13] // mid j counter[indexes[k]] += (j - mid - 1); } else { // nums[indexes[i]] > nums[indexes[j]] 构成逆序 indexes[k] = temp[j]; j++; } } } public static void main(String[] args) { int[] nums = new int[]{5, 2, 6, 1}; Solution solution = new Solution(); List<Integer> countSmaller = solution.countSmaller(nums); System.out.println(countSmaller); } }