0.分治
分而治之
1.颜色分类
75. 颜色分类 - 力扣(LeetCode)
给定一个包含红色、白色和蓝色、共
n
个元素的数组nums
,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。我们使用整数
0
、1
和2
分别表示红色、白色和蓝色。必须在不使用库内置的 sort 函数的情况下解决这个问题。
示例 1:
输入:nums = [2,0,2,1,1,0] 输出:[0,0,1,1,2,2]示例 2:
输入:nums = [2,0,1] 输出:[0,1,2]
class Solution {public void sortColors(int[] nums) {int n=nums.length;int left=-1,right=n,i=0;while(i<right){//i要扫描完if(nums[i]==0){swap(nums,++left,i++);}else if(nums[i]==2){swap(nums,--right,i);}else{i++;}}}public void swap(int[] nums,int x,int y){int tmp=nums[x];nums[x]=nums[y];nums[y]=tmp;}
}
2.快速排序
排序数组
给你一个整数数组 nums
,请你将该数组升序排列。
class Solution {public int[] sortArray(int[] nums) {qsort(nums,0,nums.length-1);return nums;}public void qsort(int[] nums,int l,int r){if(l>=r){return;}//数组分为三块//1.随机在区间内选值int key=nums[new Random().nextInt(r-l+1)+l];int left=l-1,right=r+1,i=l;while(i<right){if(nums[i]<key){swap(nums,++left,i++);}else if(nums[i]==key){i++;}else{swap(nums,--right,i);}}//[l,left],[left+1,right-1],[right,r]qsort(nums,l,left);qsort(nums,right,r);}public void swap(int[] nums,int x,int y){int tmp=nums[x];nums[x]=nums[y];nums[y]=tmp;}
}
3.数组中的第k个最大元素
215. 数组中的第K个最大元素 - 力扣(LeetCode)
给定整数数组
nums
和整数k
,请返回数组中第k
个最大的元素。请注意,你需要找的是数组排序后的第
k
个最大的元素,而不是第k
个不同的元素。你必须设计并实现时间复杂度为
O(n)
的算法解决此问题。示例 1:
输入:[3,2,1,5,6,4],
k = 2 输出: 5示例 2:
输入:[3,2,3,1,2,4,5,5,6],
k = 4 输出: 4
class Solution {public int findKthLargest(int[] nums, int k) {return qsortchoice(nums, 0, nums.length - 1, k);}public int qsortchoice(int[] nums, int l, int r, int k) {while (l == r) {return nums[l];}// 随机在范围内选择对应的数字// 随机选择一个基准元素int key = nums[new Random().nextInt(r - l + 1) + l];// 根据基准元素,使得数组分为三部分int left = l - 1, right = r + 1, i = l;while (i < right) {if (nums[i] < key) {swap(nums, ++left, i++);} else if (nums[i] == key) {i++;} else {swap(nums, --right, i);}}// 快速选择的算法,分情况讨论int b = right - left - 1, c = r - right + 1;if (c >= k) {return qsortchoice(nums, right, r, k);} else if (b + c >= k) {return key;} else {return qsortchoice(nums, l, left, k - b - c);}}public void swap(int[] nums, int x, int y) {int tmp = nums[x];nums[x] = nums[y];nums[y] = tmp;}
}
4.最小的K个数
class Solution {public int[] inventoryManagement(int[] nums, int k) {qsortchoice(nums, 0, nums.length - 1, k);int[] ret=new int[k];for(int i=0;i<k;i++){ret[i]=nums[i];}return ret;}public void qsortchoice(int[] nums, int l, int r, int k) {while (l == r) {return;}// 随机在范围内选择对应的数字// 随机选择一个基准元素int key = nums[new Random().nextInt(r - l + 1) + l];// 根据基准元素,使得数组分为三部分int left = l - 1, right = r + 1, i = l;while (i < right) {if (nums[i] < key) {swap(nums, ++left, i++);} else if (nums[i] == key) {i++;} else {swap(nums, --right, i);}}// 快速选择的算法,分情况讨论int a=left-l+1,b=right-1-left-1+1;if (a>k) {qsortchoice(nums, l, left, k);} else if (a + b >= k) {return;} else {qsortchoice(nums, right,r, k - a-b);}}public void swap(int[] nums, int x, int y) {int tmp = nums[x];nums[x] = nums[y];nums[y] = tmp;}
}
5.归并排序
912. 排序数组 - 力扣(LeetCode)
在递归的时候如果总是需要new一个变量,这是我们可以把这个变量作为全局变量。
class Solution {int[] tmp;//定义全局变量,就不用反复new了public int[] sortArray(int[] nums) {tmp=new int[nums.length];mergeSort(nums,0,nums.length-1);return nums;}public void mergeSort(int[] nums,int left,int right){if(left>=right){return;}//1.根据中间点划分区间int mid=(left+right)/2;//[left,mid][mid+1,right]//2.先把左右区间排个序 mergeSort(nums,left,mid);mergeSort(nums,mid+1,right);//3.合并两个有序数组int cur1=left,cur2=mid+1,i=0;while(cur1<=mid&&cur2<=right){tmp[i++]=nums[cur1]<=nums[cur2]?nums[cur1++]:nums[cur2++];}//处理没有遍历完的数组while(cur1<=mid){tmp[i++]=nums[cur1++];}while(cur2<=right){tmp[i++]=nums[cur2++];}//还原到nums数组上for(int j=left;j<=right;j++){nums[j]=tmp[j-left];}}
}
6.数组中的逆序对
LCR 170. 交易逆序对的总数 - 力扣(LeetCode)
class Solution {int[] tmp;public int reversePairs(int[] record) {int n=record.length;tmp=new int[n];return mergeSort(record,0,n-1);}public int mergeSort(int[] nums,int left,int right){while(left>=right){return 0;}int ret=0;//选择一个中间点,将数组进行划分int mid=(left+right)/2;//[left,mid][mid+1,right]//2.左半部分的个数+右半部分的个数+排序ret=ret+mergeSort(nums,left,mid);ret=ret+mergeSort(nums,mid+1,right);//3.一左一右的个数int cur1=left,cur2=mid+1,i=0;while(cur1<=mid&&cur2<=right){if(nums[cur1]<=nums[cur2]){tmp[i++]=nums[cur1++];}else{ret=ret+mid-cur1+1;tmp[i++]=nums[cur2++];}}//4.处理一下后面的排序while(cur1<=mid){tmp[i++]=nums[cur1++];}while(cur2<=right){tmp[i++]=nums[cur2++];}for(int j=left;j<=right;j++){nums[j]=tmp[j-left];}return ret;}
}
7.计算右侧⼩于当前元素的个数(难点)
315. 计算右侧小于当前元素的个数 - 力扣(LeetCode)
class Solution {int[] ret;int[] index; // 标记 nums 中当前元素的原始下标int[] tmpIndex;int[] tmpNums;public List<Integer> countSmaller(int[] nums) {int n = nums.length;ret = new int[n];index = new int[n];tmpIndex = new int[n];tmpNums = new int[n];// 初始化 index 数组for (int i = 0; i < n; i++) {index[i] = i;}mergeSort(nums, 0, n - 1);List<Integer> l = new ArrayList<Integer>();for (int x : ret) {l.add(x);}return l;}public void mergeSort(int[] nums, int left, int right) {if (left >= right) {return;}// 1. 根据中间元素划分区间int mid = (left + right) / 2;// [left, mid] [mid + 1, right]// 2. 处理左右两个区间mergeSort(nums, left, mid);mergeSort(nums, mid + 1, right);// 3. 处理⼀左⼀右的情况int cur1 = left, cur2 = mid + 1, i = 0;while (cur1 <= mid && cur2 <= right) { // 降序排序if (nums[cur1] <= nums[cur2]) {tmpNums[i] = nums[cur2];tmpIndex[i++] = index[cur2++];} else {ret[index[cur1]] += right - cur2 + 1; // 重点tmpNums[i] = nums[cur1];tmpIndex[i++] = index[cur1++];}}// 4. 处理剩余的排序⼯作while (cur1 <= mid) {tmpNums[i] = nums[cur1];tmpIndex[i++] = index[cur1++];}while (cur2 <= right) {tmpNums[i] = nums[cur2];tmpIndex[i++] = index[cur2++];}for (int j = left; j <= right; j++) {nums[j] = tmpNums[j - left];index[j] = tmpIndex[j - left];}}
}
8.翻转对
给定一个数组
nums
,如果i < j
且nums[i] > 2*nums[j]
我们就将(i, j)
称作一个重要翻转对。你需要返回给定数组中的重要翻转对的数量。
示例 1:
输入: [1,3,2,3,1] 输出: 2示例 2:
输入: [2,4,3,5,1] 输出: 3注意:
- 给定数组的长度不会超过
50000
。- 输入数组中的所有数字都在32位整数的表示范围内。
class Solution {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 left, int right) {if (left >= right)return 0;int ret = 0;// 1. 根据中间元素,将区间分成两部分int mid = (left + right) / 2;// [left, mid] [mid + 1, right]// 2. 求出左右两个区间的翻转对ret += mergeSort(nums, left, mid);ret += mergeSort(nums, mid + 1, right);// 3. 处理⼀左⼀右 - 先计算翻转对int cur1 = left, cur2 = mid + 1, i = left;// 降序版本while (cur1 <= mid) {while (cur2 <= right && nums[cur2] >= nums[cur1] / 2.0)cur2++;if (cur2 > right)break;ret += right - cur2 + 1;cur1++;}// 4. 合并两个有序数组cur1 = left;cur2 = mid + 1;while (cur1 <= mid && cur2 <= right)tmp[i++] = nums[cur1] <= nums[cur2] ? nums[cur2++] : nums[cur1++];while (cur1 <= mid)tmp[i++] = nums[cur1++];while (cur2 <= right)tmp[i++] = nums[cur2++];for (int j = left; j <= right; j++)nums[j] = tmp[j];return ret;}
}