个人简介:Java领域新星创作者;阿里云技术博主、星级博主、专家博主;正在Java学习的路上摸爬滚打,记录学习的过程~
个人主页:.29.的博客
学习社区:进去逛一逛~
①归并排序、快速排序 、堆排序、计数排序
🚀归并排序
⚪步骤
归并排序
:
归并排序是一种分治法(Divide and Conquer)的经典排序算法,它的基本思想是将原始数组划分成较小的数组,然后递归地对这些小数组进行排序,最后再将排好序的小数组合并成一个整体有序的数组。下面是**归并排序的详细过程: **
详细步骤
:
分解(Divide):
- 将数组一分为二,找到中间位置。
- 递归地对左右两个子数组进行分解,直到每个子数组只有一个元素。
排序(Conquer):
- 当每个子数组只包含一个元素时,认为它已经有序。
合并(Merge):
- 将两个有序的子数组合并成一个有序的数组。
- 创建一个临时数组,依次比较两个子数组的元素,将较小的元素放入临时数组。
- 如果其中一个子数组的元素已经全部放入临时数组,将另一个子数组的剩余部分直接放入临时数组。
- 将临时数组复制回原始数组的相应位置,完成合并。
⚪实现
算法实现
:
public class mergeSortTest {
//main函数,用于测试
public static void main(String[] args) {
//手动创建数组
int[] arr = new int[] {1,5,88,3,-8,7,256,-8,6,15,-96};
//使用归并排序
process(0,arr.length-1,arr);
//遍历排序后数组
for(int i = 0;i < arr.length;++i) {
System.out.print(arr[i] + " ");
}
}
//分解 + 合并(分解合并过程中已完成排序):
public static void process(int L, int R, int[] arr) {
if(L == R) return; //下标重合,结束
int mid = L + ((R - L) >> 1); //获取中间下标mid、 (R - L) >> 1 等同于 (R - L) / 2
process(L , mid, arr); //分解左子数组
process(mid + 1, R, arr); //分解右子数组
merge(L, mid, R, arr); //合并
}
//合并:
public static void merge(int L, int mid, int R, int[] arr) {
int[] help = new int[R - L + 1]; //帮组数组
int p1 = L; //左子数组起始下标
int p2 = mid + 1; //右子数组起始下标
int i = 0; //帮组数组起始下标
//合并,两个子数组元素按顺序比较,较小的元素放入帮组数组,重复此步骤。
while(p1 <= mid && p2 <= R) {
help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
}
while(p1 <= mid) {
help[i++] = arr[p1++];
}
while(p2 <= R) {
help[i++] = arr[p2++];
}
//将帮助数组的元素赋值回元素组。
for(i = 0;i < help.length; ++i) {
arr[i + L] = help[i];
}
}
}
⚪复杂度
复杂度分析
:
- 时间复杂度: O(n log n) - 归并排序始终都是O(n log n)的,无论最坏情况还是平均情况。
- 空间复杂度: O(n) - 归并排序需要一个与原始数组同样大小的辅助数组来进行合并操作,因此空间复杂度为O(n)。
🚀快速排序
⚪步骤
快速排序
:
快速排序(Quick Sort)是一种常用的基于分治思想的排序算法。它的基本思想是通过一趟排序将待排序的记录分割成独立的两部分,其中一部分的所有记录均比另一部分的记录小,然后分别对这两部分记录继续进行排序,以达到整个序列有序的目的。快速排序的核心是选择一个基准元素,将小于等于基准的元素放在左边,大于基准的元素放在右边,然后对左右两个子序列分别递归地应用相同的方法。
详细步骤
:
-
选择基准元素:
- 从待排序数组中选择一个元素作为基准元素。通常选择第一个元素、最后一个元素或者中间元素。(本文等概率随机选取一个元素作为基准,避免最坏复杂度的发生)
-
分区(Partition):
- 将数组中小于等于基准的元素放在基准的左边,大于基准的元素放在右边。这一步通常称为分区操作。
- 使用两个指针,一个在数组的起始位置,一个在数组的结束位置,分别向中间移动,交换不符合条件的元素,直到两个指针相遇。
-
递归排序:
- 对基准左右两侧的子数组进行递归排序。重复以上步骤,直到每个子数组的大小为1或0。
⚪实现
算法实现
:
public class Qsort {
//main函数,用于测试
public static void main(String[] args) {
//手动创建数组,用于测试
int[] arr = new int[] {1,5,99,-29,698,-111,0,63,88,3,3,7,1,-8,7,256};
//若数组为空,长度<2等情况,无需排序
if(arr == null || arr.length < 2) {
return;
}
//使用快速排序,传入数组头部和尾部下标
quickSort(0, arr.length-1, arr);
for(int i = 0;i < arr.length;++i) {
System.out.print(arr[i] + " ");
}
}
//快速排序函数:
public static void quickSort(int L, int R, int[] arr) {
//下表不越界情况下:进行基准选取、划分、递归排序
if(L < R) {
//使用random函数,随机选取一个元素作为基准,使得复杂度总是O(n log n)
swap(R, L + (int)(Math.random() * (R - L + 1)), arr);
//进行划分,获取到两个边界值:小于基准的边界less,大于基准的边界more
//p[0] = less + 1 ,p[1] = more - 1
int[] p = partition(L, R, arr);
//分别对划分后的子数组继续进行上述操作,是为递归排序
quickSort(L, p[0] - 1, arr);
quickSort(p[1] + 1, R, arr);
}
}
//划分函数:
public static int[] partition(int L, int R, int[] arr) {
//定义小于基准的边界less,大于基准的边界more
int less = L - 1;
int more = R;
//从数组起始下标L开始遍历,直到超过大于基准的边界more,即下标越界
while(L < more) {
//当前元素小于基准,与less边界外的元素交换,less边界扩展1位,下一元素作为当前元素
if(arr[L] < arr[R]) {
swap(++less, L++, arr);
//当前元素大于基准,与more边界外的元素交换,more边界扩展1位,交换后,当前元素未被比较,无需后移
}else if(arr[L] > arr[R]) {
swap(--more, L, arr);
//等于基准,无需交换,直接后移
}else {
++L;
}
}
//划分完成后,将数组末尾的基准元素与more边界位置元素交换,真正的more边界变为more + 1
swap(more, R, arr);
//将边界存入数组返回:
return new int[] {less + 1, more};
}
//交换函数:
public static void swap(int a, int b, int[] arr) {
int tmp = arr[a];
arr[a] = arr[b];
arr[b] = tmp;
}
}
⚪复杂度
复杂度分析
:
- 时间复杂度: 平均情况下为O(n log n),最坏情况为O(n^2)。快速排序的性能高度依赖于选择的基准元素。
- 空间复杂度: O(log n) - 快速排序是一种原地排序算法,只需要常数级别的额外空间用于递归调用的栈。
🚀堆排序
⚪步骤
堆排序
:
堆排序(Heap Sort)是一种基于二叉堆数据结构的排序算法,它利用堆的性质进行排序。堆是一个完全二叉树,可以分为最大堆和最小堆两种类型。在最大堆中,父节点的值大于或等于其子节点的值;而在最小堆中,父节点的值小于或等于其子节点的值。
以下是堆排序的详细步骤:
详细步骤
:
-
建堆(Heapify):
- 将待排序的数组构建成一个堆。建堆的过程是从最后一个非叶子节点开始,依次向上调整各个子树,使其满足堆的性质。
-
排序:
- 交换堆顶元素(最大或最小元素)和堆的最后一个元素,然后将堆的大小减 1。
- 再次调整堆,使其满足堆的性质。
- 重复上述步骤,直到堆的大小为 1,排序完成。
⚪实现
代码实现
:
public class heapSortTest {
//main函数、测试用
public static void main(String[] args) {
int[] arr = new int[]{1,5,88,9,-88,-99,-685,78,4,3,3,7,1,-8,7,256};
heapSort(arr);
for(int i = 0;i < arr.length;++i) {
System.out.print(arr[i] + " ");
}
}
// 堆排序,排序函数
public static void heapSort(int[] arr) {
if(arr == null || arr.length < 2) return;
for(int i = arr.length - 1; i >= 0; --i) {
heapify(arr, i, arr.length - 1);
}
int heapSize = arr.length;
swap(arr, 0, --heapSize);
while(heapSize > 0) {
heapify(arr, 0, heapSize);
swap(arr, 0, --heapSize);
}
}
//heapify,建堆函数
public static void heapify(int[] arr, int index, int heapSize) {
//获取左孩子下标
int left = 2 * index + 1;
//left < heapSize : 当前节点存在左孩子
while(left < heapSize) {
// 获取左孩子、右孩子中最大值
int largest = left + 1 < heapSize && arr[left] < arr[left + 1] ? left + 1 : left;
// 较大孩子元素与父节点元素比较,更新较大值下标
largest = arr[index] < arr[largest] ? largest : index;
// 父节点最大,无法下移,直接停止
if(index == largest) break;
// 父节点小于较大孩子,下移
swap(arr, index, largest);
// 维护父节点和左孩子
index = largest;
left = 2 * index + 1;
}
}
//交换函数
public static void swap(int[] arr, int a, int b) {
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}
⚪复杂度
复杂度分析
:
- 时间复杂度: 建堆操作的时间复杂度为O(n),排序操作的时间复杂度为O(n log n)。因此,堆排序的总体时间复杂度为O(n log n)。
- 空间复杂度: 堆排序是一种原地排序算法,只需使用常数级别的额外空间,因此空间复杂度为O(1)。
🚀912. 排序数组
[⚪点击跳转【LeetCode 912. 排序数组】](912. 排序数组 - 力扣(LeetCode))
题目
:
给你一个整数数组
nums
,请你将该数组升序排列。示例 1:
输入:nums = [5,2,3,1] 输出:[1,2,3,5]
示例 2:
输入:nums = [5,1,1,2,0,0] 输出:[0,0,1,1,2,5]
提示:
1 <= nums.length <= 5 * 104
-5 * 104 <= nums[i] <= 5 * 104
解法一:归并排序
:
//使用归并排序
class Solution {
public int[] sortArray(int[] nums) {
int length = nums.length;
mergeSort(nums,0,nums.length - 1);
return nums;
}
public void mergeSort(int[] arr,int l,int r){
//子数组长度为1,停止
if(l == r) return;
//取中点
int mid = l + ((r - l) >> 1);
//递归排序左子数组
mergeSort(arr,l,mid);
//递归排序右子数组
mergeSort(arr,mid + 1,r);
//合并两个排序好的数组
merge(arr,l,mid,r);
}
public void merge(int[] arr,int l,int mid,int r){
//help数组
int[] help = new int[r - l + 1];
//help数组下标
int i = 0;
//左子数组头p1
int p1 = l;
//右子数组头p2
int p2 = mid + 1;
//合并两个有序数组,存入help数组
while(p1 <= mid && p2 <= r){
help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
}
while(p1 <= mid){
help[i++] = arr[p1++];
}
while(p2 <= r){
help[i++] = arr[p2++];
}
//将help数组的元素赋值回目标数组
for(int j = 0;j < help.length;++j){
arr[l + j] = help[j];
}
}
}
解法二:快速排序
:
class Solution {
//快排
public int[] sortArray(int[] nums) {
if(nums == null || nums.length < 2) return nums; //空或长度1,无需排序
qSort(nums,0,nums.length - 1); //快排
return nums;
}
public static void qSort(int[] arr,int l,int r){
//数组头尾不冲突
if(l < r){
//利用random等概率随机获取基准 与尾部元素交换位置
swap(arr,r,l + (int)(Math.random() * (r - l + 1)));
//得到长度二的数组,两个元素分别代表大于基准元素与小于基准元素的边界
int[] p = partition(arr,l,r);
qSort(arr,l,p[0]);
qSort(arr,p[1],r);
}
}
//划分函数
public static int[] partition(int[] arr,int l,int r){
int less = l - 1; //小于基准的边界
int more = r; //大于基准的边界
while(l < more){
if(arr[l] < arr[r]){
swap(arr,++less,l++);
}else if(arr[l] > arr[r]){
swap(arr,--more,l);
}else{
++l;
}
}
swap(arr,more,r);
return new int[]{less,more + 1};
}
//交换
public static void swap(int[] arr,int a,int b){
int tmp = arr[a];
arr[a] = arr[b];
arr[b] = tmp;
}
}
解法三:堆排序
:
class Solution {
public int[] sortArray(int[] nums) {
heapSort(nums);
return nums;
}
public static void heapSort(int[] arr){
if(arr == null || arr.length < 2) return;
for(int i = arr.length - 1; i >= 0; --i){
heapify(arr, i, arr.length);
}
int heapSize = arr.length;
swap(arr, 0, --heapSize);
while(heapSize > 0){
heapify(arr, 0, heapSize);
swap(arr, 0, --heapSize);
}
}
public static void heapify(int[] arr, int index, int heapSize){
int left = 2 * index + 1;
while(left < heapSize){
int largest = left + 1 < heapSize && arr[left] < arr[left+1] ? left + 1 : left;
largest = arr[index] < arr[largest] ? largest : index;
if(largest == index) break;
swap(arr, index, largest);
index = largest;
left = 2 * index + 1;
}
}
public static void swap(int[] arr, int a, int b){
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}
🚀315. 计算右侧小于当前元素的个数
⚪点击跳转:315. 计算右侧小于当前元素的个数
给你一个整数数组
nums
,按要求返回一个新数组counts
。数组counts
有该性质:counts[i]
的值是nums[i]
右侧小于nums[i]
的元素的数量。示例 1:
输入:nums = [5,2,6,1] 输出:[2,1,1,0] 解释: 5 的右侧有 2 个更小的元素 (2 和 1) 2 的右侧仅有 1 个更小的元素 (1) 6 的右侧有 1 个更小的元素 (1) 1 的右侧有 0 个更小的元素
示例 2:
输入:nums = [-1] 输出:[0]
示例 3:
输入:nums = [-1,-1] 输出:[0,0]
提示:
1 <= nums.length <= 105
-104 <= nums[i] <= 104
利用归并排序性质,完成计数
:
// 利用归并排序的特性, 计算 nums[i] 右侧小于 nums[i] 的元素的数量。
class Solution {
static int[] counts; // 题目要求的counts数组
static int[] index; // 下标数组,记录nums元素的移动,即:元素移动,下标做同样操作。
public List<Integer> countSmaller(int[] nums) {
List<Integer> list = new ArrayList<>(); //创建集合,因为:函数返回值为List<Integer>
if(nums == null) return list; //数组为空,直接返回空集合
if(nums.length == 1){ //数组长度=1
list.add(0); //右侧没有小于nums[i]的元素,所以nums[i]=0,添加到集合中
return list; //直接返回
}
//初始化counts和index
int n = nums.length;
this.counts = new int[n];
this.index = new int[n];
//为下标数组元素 附上 下标值
for(int i = 0;i < n; ++i){
index[i] = i;
}
//进行归并排序,在排序过程中完成计数
mergeSort(nums, 0, n - 1);
//将得到的符合规则的counts数组元素赋值给集合再返回,因为:函数返回值为List<Integer>
for(int count : counts) list.add(count);
return list;
}
//归并排序:
public static void mergeSort(int[] arr, int l, int r){
if(l == r) return;
int mid = l + ((r - l) >> 1);
mergeSort(arr, l, mid);
mergeSort(arr, mid + 1, r);
merge(arr, l, mid, r);
}
//合并有序子序列
public static void merge(int[] arr, int l, int mid, int r){
int[] help = new int[r - l + 1]; //元素的help数组,用于合并有序子序列
int[] helpI = new int[r - l + 1]; //下标的help数组,用于记录合并有序子序列时元素的移动
int i = 0; //下标
int p1 = l; //左子序列头元素下标
int p2 = mid + 1; //右子序列头元素下标
while(p1 <= mid && p2 <= r){
//按照逆序,进行归并排序的合并
if(arr[p1] > arr[p2]){
//因为逆序,所以p2到R范围内的元素是降序的,即:
//当前情况下nums[i] 右侧小于 nums[i] 的元素的数量 = r - p2 + 1
//在排序过程中,累加所有情况下的r - p2 + 1,就能的到总数。
counts[index[p1]] += r - p2 + 1;
help[i] = arr[p1];
helpI[i++] = index[p1++];
}else{
help[i] = arr[p2];
helpI[i++] = index[p2++];
}
}
while(p1 <= mid){
help[i] = arr[p1];
helpI[i++] = index[p1++];
}
while(p2 <= r){
help[i] = arr[p2];
helpI[i++] = index[p2++];
}
//将help数组中的元素 赋值回原数组中,完成合并。
for(i = 0;i < help.length; ++i){
arr[i + l] = help[i];
index[i + l] = helpI[i];
}
}
}
🚀561. 数组拆分
⚪点击跳转:561. 数组拆分
给定长度为
2n
的整数数组nums
,你的任务是将这些数分成n
对, 例如(a1, b1), (a2, b2), ..., (an, bn)
,使得从1
到n
的min(ai, bi)
总和最大。返回该 最大总和 。
示例 1:
输入:nums = [1,4,3,2] 输出:4 解释:所有可能的分法(忽略元素顺序)为: 1. (1, 4), (2, 3) -> min(1, 4) + min(2, 3) = 1 + 2 = 3 2. (1, 3), (2, 4) -> min(1, 3) + min(2, 4) = 1 + 2 = 3 3. (1, 2), (3, 4) -> min(1, 2) + min(3, 4) = 1 + 3 = 4 所以最大总和为 4
示例 2:
输入:nums = [6,2,6,5,1,2] 输出:9 解释:最优的分法为 (2, 1), (2, 5), (6, 6). min(2, 1) + min(2, 5) + min(6, 6) = 1 + 2 + 6 = 9
提示:
1 <= n <= 104
nums.length == 2 * n
-104 <= nums[i] <= 104
题解(归并排序)
:
//归并排序
class Solution {
public int arrayPairSum(int[] nums) {
//排序数组(这里使用归并排序,还推荐使用堆排序、快速排序)
mergeSort(nums, 0, nums.length - 1);
//根据分析,有序的数组分成n对,就能满足题意
int ans = 0;
for(int i = 0;i < nums.length; i += 2){
//将每队的min(ai, bi)累加起来,得到总和
ans += nums[i];
}
//返回结果
return ans;
}
public static void mergeSort(int[] arr, int l, int r){
if(l == r) return;
int mid = l + ((r - l) >> 1);
mergeSort(arr, l, mid);
mergeSort(arr, mid + 1, r);
merge(arr, l, mid, r);
}
public static void merge(int[] arr, int l,int mid, int r){
int[] help = new int[r - l + 1];
int p1 = l;
int p2 = mid + 1;
int i = 0;
while(p1 <= mid && p2 <= r){
help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
}
while(p1 <= mid){
help[i++] = arr[p1++];
}
while(p2 <= r){
help[i++] = arr[p2++];
}
for(i = 0;i < help.length; ++i){
arr[i + l] = help[i];
}
}
}
题解(堆排序)
:
class Solution {
public int arrayPairSum(int[] nums) {
int n = nums.length;
heapSort(nums, n); //堆排序
int ans = 0;
for(int i = 0;i < n; i += 2){
ans += nums[i];
}
return ans;
}
public static void heapSort(int[] arr, int n){
if(arr == null || n < 2) return;
for(int i = n - 1; i >= 0; --i){
heapify(arr, i, n);
}
int heapSize = n;
swap(arr, 0, --heapSize);
while(heapSize > 0){
heapify(arr, 0, heapSize);
swap(arr, 0, --heapSize);
}
}
public static void heapify(int[] arr, int index, int heapSize){
int left = 2 * index + 1;
while(left < heapSize){
int largest = left + 1 < heapSize && arr[left + 1] > arr[left] ? left + 1 : left;
largest = arr[index] > arr[largest] ? index : largest;
if(index == largest) break; //无法下移,停止
swap(arr, index, largest); //可以下移,交换
index = largest; //交换完后完成下移
left = 2 * index + 1; //维护左孩子
}
}
public static void swap(int[] arr, int a, int b){
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}
题解(快速排序)【速度最快】
:
class Solution {
public int arrayPairSum(int[] nums) {
int n = nums.length;
quickSort(nums, 0, n - 1);
int ans = 0;
for(int i = 0;i < n; i += 2){
ans += nums[i];
}
return ans;
}
public static void quickSort(int[] arr, int l, int r){
if(l < r){
swap(arr, r, l + (int)(Math.random() * (r - l + 1)));
int[] p = partition(arr, l, r);
quickSort(arr, l, p[0]);
quickSort(arr,p[1], r);
}
}
public static int[] partition(int[] arr, int l, int r){
int less = l - 1;
int more = r;
while(l < more){
if(arr[l] < arr[r]){
swap(arr, ++less, l++);
}else if(arr[l] > arr[r]){
swap(arr, --more, l);
}else{
++l;
}
}
swap(arr, more, r);
return new int[]{less, more + 1};
}
public static void swap(int[] arr, int a, int b){
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}
🚀1122. 数组的相对排序(计数排序)
⚪点击跳转:1122. 数组的相对排序
给你两个数组,
arr1
和arr2
,arr2
中的元素各不相同,arr2
中的每个元素都出现在arr1
中。对
arr1
中的元素进行排序,使arr1
中项的相对顺序和arr2
中的相对顺序相同。未在arr2
中出现过的元素需要按照升序放在arr1
的末尾。示例 1:
输入:arr1 = [2,3,1,3,2,4,6,7,9,2,19], arr2 = [2,1,4,3,9,6] 输出:[2,2,2,1,4,3,3,9,6,7,19]
示例 2:
输入:arr1 = [28,6,22,8,44,17], arr2 = [22,28,8,6] 输出:[22,28,8,6,17,44]
提示:
1 <= arr1.length, arr2.length <= 1000
0 <= arr1[i], arr2[i] <= 1000
arr2
中的元素arr2[i]
各不相同arr2
中的每个元素arr2[i]
都出现在arr1
中
题解(计数排序)
:
计数排序是一种非比较性的整数排序算法,适用于待排序元素的范围较小的情况。该算法的基本思想是统计每个元素的出现次数,然后根据这些统计信息将元素放回正确的位置。
class Solution {
//计数排序
public int[] relativeSortArray(int[] arr1, int[] arr2) {
//题目给定元素大小不超过1000,就可以设置一个1001长度的数组来进行计数排序
int[] arr = new int[1001]; //计数数组
int[] ans = new int[arr1.length]; //排序数组
int index = 0; //排序数组的下标
//遍历arr1,遍历到元素,就使arr数组对应下标的值+1
for(int num : arr1){
arr[num] += 1;
}
//遍历arr2数组,先将与arr2相对顺序元素放入排序数组
for(int num : arr2){
for(int i = 0;i < arr[num]; ++i){
ans[index++] = num;
}
arr[num] = 0; //维护计数数组
}
//遍历计数数组,将剩下的元素排序好并放入排序数组
for(int k = 0;k < 1001; ++k){
for(int i = 0;i < arr[k]; ++i){
ans[index++] = k;
}
}
return ans;
}
}
🚀268. 丢失的数字(计数排序)
⚪点击跳转:268. 丢失的数字
给定一个包含
[0, n]
中n
个数的数组nums
,找出[0, n]
这个范围内没有出现在数组中的那个数。示例 1:
输入:nums = [3,0,1] 输出:2 解释:n = 3,因为有 3 个数字,所以所有的数字都在范围 [0,3] 内。2 是丢失的数字,因为它没有出现在 nums 中。
示例 2:
输入:nums = [0,1] 输出:2 解释:n = 2,因为有 2 个数字,所以所有的数字都在范围 [0,2] 内。2 是丢失的数字,因为它没有出现在 nums 中。
示例 3:
输入:nums = [9,6,4,2,3,5,7,0,1] 输出:8 解释:n = 9,因为有 9 个数字,所以所有的数字都在范围 [0,9] 内。8 是丢失的数字,因为它没有出现在 nums 中。
示例 4:
输入:nums = [0] 输出:1 解释:n = 1,因为有 1 个数字,所以所有的数字都在范围 [0,1] 内。1 是丢失的数字,因为它没有出现在 nums 中。
提示:
n == nums.length
1 <= n <= 104
0 <= nums[i] <= n
nums
中的所有数字都 独一无二
题解(计数排序)
class Solution {
public int missingNumber(int[] nums) {
int ans = -1; //没有出现在数组中的那个数。
int n = nums.length; //题目提到的n
int[] arr = new int[n + 1]; //计数数组,用于记录元素出现的次数
for(int num : nums){ //使用计数数组,记录并排序nums数组元素
arr[num] += 1;
}
for(int i = 0;i <= n; ++i){ //遍历计数数组,值为0表示未出现
if(arr[i] == 0){
ans = i;
break;
}
}
return ans;
}
}
🚀215. 数组中的第K个最大元素
⚪点击跳转:215. 数组中的第K个最大元素
给定整数数组
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
提示:
1 <= k <= nums.length <= 105
-104 <= nums[i] <= 104
题解(快速排序)
:
class Solution {
public int findKthLargest(int[] nums, int k) {
quickSort(nums, 0, nums.length - 1);
int ans = Integer.MIN_VALUE;
for(int i = 0;i <= nums.length - k; ++i){
ans = nums[i];
}
return ans;
}
public static void quickSort(int[] arr, int l, int r){
if(l < r){
swap(arr, r, l + (int)(Math.random() * (r - l)));
int[] p = partition(arr, l, r);
quickSort(arr, l, p[0]);
quickSort(arr, p[1], r);
}
}
public static int[] partition(int[] arr, int l, int r){
int less = l - 1;
int more = r;
while(l < more){
if(arr[l] < arr[r]){
swap(arr, ++less, l++);
}else if(arr[l] > arr[r]){
swap(arr, --more, l);
}else{
++l;
}
}
swap(arr, more, r);
return new int[]{less, more + 1};
}
public static void swap(int[] arr, int a, int b){
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}
🚀347. 前 K 个高频元素
⚪点击跳转:347. 前 K 个高频元素
给你一个整数数组
nums
和一个整数k
,请你返回其中出现频率前k
高的元素。你可以按 任意顺序 返回答案。示例 1:
输入: nums = [1,1,1,2,2,3], k = 2 输出: [1,2]
示例 2:
输入: nums = [1], k = 1 输出: [1]
提示:
1 <= nums.length <= 105
k
的取值范围是[1, 数组中不相同的元素的个数]
- 题目数据保证答案唯一,换句话说,数组中前
k
个高频元素的集合是唯一的
题解(优先队列[堆排序] + hash表)
:
class Solution {
public int[] topKFrequent(int[] nums, int k) {
PriorityQueue<int[]> pq = new PriorityQueue<>((a,b) -> (b[1] - a[1]));
Map<Integer, Integer> map = new HashMap<>();
int[] ans = new int[k];
for(int num : nums){
if(!map.containsKey(num)){
map.put(num,1);
}else{
map.put(num,map.get(num).intValue() + 1);
}
}
for(Map.Entry<Integer, Integer> entry : map.entrySet()){
pq.offer(new int[]{entry.getKey(), entry.getValue()});
}
for(int i = 0;i < k; ++i){
ans[i] = pq.poll()[0];
}
return ans;
}
}
🚀LCR 159. 库存管理 III(计数排序)
⚪点击跳转:LCR 159. 库存管理 III
仓库管理员以数组
stock
形式记录商品库存表,其中stock[i]
表示对应商品库存余量。请返回库存余量最少的cnt
个商品余量,返回 顺序不限。示例 1:
输入:stock = [2,5,7,4], cnt = 1 输出:[2]
示例 2:
输入:stock = [0,2,3,6], cnt = 2 输出:[0,2] 或 [2,0]
提示:
0 <= cnt <= stock.length <= 10000 0 <= stock[i] <= 10000
题解(计数排序)
:
class Solution {
public int[] inventoryManagement(int[] stock, int cnt) {
int[] help = new int[10001];
for(int num : stock){
help[num] += 1;
}
int[] ans = new int[cnt];
int index = 0;
a:for(int i = 0;i < 10001; ++i){
for(int j = 0;j < help[i]; ++j){
if(index < cnt){
ans[index++] = i;
}else{
break a;
}
}
}
return ans;
}
}
题解(快速排序)
:
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
int[] ans = new int[k];
qSort(arr,0,arr.length - 1);
for(int i = 0;i < k;++i){
ans[i] = arr[i];
}
return ans;
}
public static void qSort(int[] arr,int l,int r){
if(l < r){
swap(arr,r,l + (int)(Math.random() * (r - l + 1)));
int[] p = partition(arr,l,r);
qSort(arr,l,p[0]-1);
qSort(arr,p[1]+1,r);
}
}
public static int[] partition(int[] arr,int l,int r){
int less = l - 1;
int more = r;
while(l < more){
if(arr[l] < arr[r]){
swap(arr,++less,l++);
}else if(arr[l] > arr[r]){
swap(arr,l,--more);
}else{
++l;
}
}
swap(arr,r,more);
return new int[]{less+1,more};
}
public static void swap(int[] arr,int a,int b){
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}
🚀LCR 170. 交易逆序对的总数
⚪点击跳转:LCR 170. 交易逆序对的总数
在股票交易中,如果前一天的股价高于后一天的股价,则可以认为存在一个「交易逆序对」。请设计一个程序,输入一段时间内的股票交易记录
record
,返回其中存在的「交易逆序对」总数。示例 1:
输入:record = [9, 7, 5, 4, 6] 输出:8 解释:交易中的逆序对为 (9, 7), (9, 5), (9, 4), (9, 6), (7, 5), (7, 4), (7, 6), (5, 4)。
限制:
0 <= record.length <= 50000
归并排序 题解
:
class Solution {
int ans = 0;
public int reversePairs(int[] record) {
if(record == null || record.length < 2) return ans;
mergeSort(record, 0, record.length - 1);
return ans;
}
public void mergeSort(int[] arr, int l, int r){
if(l == r) return;
int mid = l + ((r - l) >> 1);
mergeSort(arr, l, mid);
mergeSort(arr, mid + 1, r);
merge(arr, l, mid, r);
}
public void merge(int[] arr, int l, int mid, int r){
int[] help = new int[r - l + 1];
int p1 = l;
int p2 = mid + 1;
int i = 0;
while(p1 <= mid && p2 <= r){
if(arr[p1] > arr[p2]){
ans += r - p2 + 1;
help[i++] = arr[p1++];
}else{
help[i++] = arr[p2++];
}
}
while(p1 <= mid){
help[i++] = arr[p1++];
}
while(p2 <= r){
help[i++] = arr[p2++];
}
for(i = 0;i < help.length; ++i){
arr[i + l] = help[i];
}
}
}
文章来源:https://www.toymoban.com/news/detail-767138.html
文章来源地址https://www.toymoban.com/news/detail-767138.html
到了这里,关于①归并排序、快速排序 、堆排序、计数排序[算法、代码模板、面试题]的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!