算法奇妙屋(四)-归并分治
文章目录
- 一. 力扣 912. 排序数组
- 1. 题目
- 2. 算法原理
- 3.代码
- 二. 力扣 LCR 170. 交易逆序对的总数
- 1. 题目
- 2. 算法原理
- (1) 方法一
- (2) 方法二
- 3. 代码
- (1) 方法一代码:
- (2) 方法二代码:
- 三. 力扣 315. 计算右侧小于当前元素的个数
- 1. 题目
- 2. 算法原理
- 3. 代码
- 四. 力扣 493. 翻转对
- 1. 题目
- 2. 算法原理
- (1) 方法一(降序):
- (2) 方法二(升序):
- 3. 代码
- (1) 方法一(降序)代码:
- (2) 方法二(升序)代码:
这个篇章我们基于归并排序,并结合分治思想来解决问题
一. 力扣 912. 排序数组
1. 题目
2. 算法原理
归并排序我们应该不陌生, 这里我们只是复习归并排序的算法原理
3.代码
public int[] sortArray(int[] nums) {mergeSort(nums,0,nums.length-1);return nums;}public void mergeSort(int[] nums, int s, int e) {if (s >= e) {return;}int mid = (s + e) / 2;// 递归左区间mergeSort(nums,s,mid);// 递归右区间mergeSort(nums,mid + 1,e);// 递归完成后, 给两个数组排序(数组并不是真实数组,而是可以看成两个数组)int left1 = s;int left2 = mid + 1;int i = 0;int[] tmp = new int[e - s + 1];while (left1 <= mid && left2 <= e) {if (nums[left1] <= nums[left2]) {tmp[i++] = nums[left1++];}else {tmp[i++] = nums[left2++];}}while(left1 <= mid) {tmp[i++] = nums[left1++];}while(left2 <= e) {tmp[i++] = nums[left2++];}// 将tmp数组的值放入nums中for (int j = 0; j < e - s + 1; j++) {nums[j + s] = tmp[j];}}
二. 力扣 LCR 170. 交易逆序对的总数
1. 题目
2. 算法原理
(1) 方法一
(2) 方法二
3. 代码
(1) 方法一代码:
int ret = 0;int[] tmp ;public int reversePairs(int[] nums) {tmp = new int[nums.length];mergeSort(nums, 0,nums.length - 1);return ret;}public void mergeSort(int[] nums, int start, int end) {if (start >= end) {return;}int mid = (end + start) / 2;// 递归左面[s,mid]mergeSort(nums, start, mid);// 递归右面[mid+1,e]mergeSort(nums, mid + 1, end);// 递归结束, 开始排序, 并统计int left1 = start;int left2 = mid + 1;int i = 0;while (left1 <= mid && left2 <= end) {if (nums[left1] <= nums[left2]) {tmp[i++] = nums[left1++];}else {ret += mid - left1 + 1;tmp[i++] = nums[left2++];}}while (left1 <= mid) {tmp[i++] = nums[left1++];}while (left2 <= end) {tmp[i++] = nums[left2++];}for (int j = 0; j < end - start + 1; j++) {nums[j + start] = tmp[j];}}
(2) 方法二代码:
int ret = 0;int[] tmp ;public int reversePairs(int[] nums) {tmp = new int[nums.length];mergeSort(nums, 0,nums.length - 1);return ret;}public void mergeSort(int[] nums, int start, int end) {if (start >= end) {return;}int mid = (end + start) / 2;// 递归左面[s,mid]mergeSort(nums, start, mid);// 递归右面[mid+1,e]mergeSort(nums, mid + 1, end);// 递归结束, 开始排序, 并统计int left1 = start;int left2 = mid + 1;int i = 0;while (left1 <= mid && left2 <= end) {if (nums[left1] <= nums[left2]) {tmp[i++] = nums[left2++];}else {ret += end - left2 + 1;tmp[i++] = nums[left1++];}}while (left1 <= mid) {tmp[i++] = nums[left1++];}while (left2 <= end) {tmp[i++] = nums[left2++];}for (int j = 0; j < end - start + 1; j++) {nums[j + start] = tmp[j];}}
三. 力扣 315. 计算右侧小于当前元素的个数
1. 题目
2. 算法原理
3. 代码
int[] tmpNums;int[] tmpIndex;int[] index;int[] ret;public List<Integer> countSmaller(int[] nums) {List<Integer> list = new ArrayList<>();int n = nums.length;tmpNums = new int[n];tmpIndex = new int[n];index = new int[n];ret = new int[n];for (int i = 0; i < n; i++) {index[i] = i;}mergeSort(nums, 0, n - 1);for (int x : ret) {list.add(x);}return list;}public void mergeSort(int[] nums, int start, int end) {if (start >= end) {return;}int mid = (start + end) / 2;// 计算左面个数mergeSort(nums, start, mid);// 计算右面个数mergeSort(nums, mid + 1, end);// 计算一左一右int left1 = start;int left2 = mid + 1;int i = 0;while (left1 <= mid && left2 <= end) {if (nums[left1] <= nums[left2]) {tmpNums[i] = nums[left2];tmpIndex[i++] = index[left2++];}else {ret[index[left1]] += end - left2 + 1;tmpNums[i] = nums[left1];tmpIndex[i++] = index[left1++];}}while (left1 <= mid) {tmpNums[i] = nums[left1];tmpIndex[i++] = index[left1++];}while (left2 <= end) {tmpNums[i] = nums[left2];tmpIndex[i++] = index[left2++];}// 将tmp数组的值放回到原数组其中, 排序工作for (int j = start; j <= end; j++) {nums[j] = tmpNums[j - start];index[j] = tmpIndex[j - start];}}
四. 力扣 493. 翻转对
1. 题目
题目意思大白话解释就是前面有多少数大于后面数值的2倍
2. 算法原理
(1) 方法一(降序):
(2) 方法二(升序):
3. 代码
(1) 方法一(降序)代码:
我们要注意,这道题里面的测试用例输入数组中的所有数字都在32位整数的表示范围内, 但是我们×2后那么就会超出32位, 所以我们用除以2的方式
int[] tmp;public int reversePairs(int[] nums) {int n = nums.length;tmp = new int[n];return mergeSort(nums, 0, n - 1);}public int mergeSort(int[] nums, int start, int end) {if (start >= end) {return 0;}int ret = 0;int mid = (start + end) / 2;// 先计算左面ret += mergeSort(nums, start, mid);// 再计算右面ret += mergeSort(nums, mid + 1, end);// 最后计算一左一右int left1 = start;int left2 = mid + 1;while (left1 <= mid && left2 <= end) {if (nums[left1] / 2.0 > nums[left2]) {ret += end - left2 + 1;left1++;}else {left2++;}}// 放入tmp中排序left1 = start;left2 = mid + 1;int i = 0;while (left1 <= mid && left2 <= end) {tmp[i++] = nums[left1] > nums[left2] ? nums[left1++] : nums[left2++];}while (left1 <= mid) {tmp[i++] = nums[left1++];}while (left2 <= end) {tmp[i++] = nums[left2++];}// 合并数组for (int j = start; j <= end; j++) {nums[j] = tmp[j - start];}return ret;}
(2) 方法二(升序)代码:
int[] tmp;public int reversePairs(int[] nums) {int n = nums.length;tmp = new int[n];return mergeSort(nums, 0, n - 1);}public int mergeSort(int[] nums, int start, int end) {if (start >= end) {return 0;}int ret = 0;int mid = (start + end) / 2;// 先计算左面ret += mergeSort(nums, start, mid);// 再计算右面ret += mergeSort(nums, mid + 1, end);// 最后计算一左一右int left1 = start;int left2 = mid + 1;while (left1 <= mid && left2 <= end) {if (nums[left1] / 2.0 > nums[left2]) {ret += mid - left1 + 1;left2++;}else {left1++;}}// 放入tmp中排序left1 = start;left2 = mid + 1;int i = 0;while (left1 <= mid && left2 <= end) {tmp[i++] = nums[left1] <= nums[left2] ? nums[left1++] : nums[left2++];}while (left1 <= mid) {tmp[i++] = nums[left1++];}while (left2 <= end) {tmp[i++] = nums[left2++];}// 合并数组for (int j = start; j <= end; j++) {nums[j] = tmp[j - start];}return ret;}