本文已参与「新人创作礼」活动,一起开启掘金创作之路。
题目链接:1619. 删除某些元素后的数组均值
题目描述【easy】
解题思路
1.初见
乍一看,这题第一思路就是直接排序,采用快速排序,变态一点使用原地快排,时间复杂度O(NlogN),直接搞定;后来仔细审题,发现是要去除给定数组的前5%以及后5%的数之后取平均数,在想是不是有更快的方式,理所当然的觉得会有O(NlogN)更优即O(N)级别的方法。
代码-1.初见-原地快排
/**
* QuickSort Internal
* 1.select pivot
* 2.right to left find first item lt pivot rj
* 3.left to right find first item gt pivot li
* 4.swap rj li
* 5.repeat 2-4 until i >= j
*/
private void quickSortInternal(int[] arr, int lo, int hi) {
if (lo >= hi) {
return;
}
int pivot = arr[lo];
int i = lo;
int j = hi;
while (i < j) {
while (i < j && arr[j] > pivot) {
j--;
}
while (i < j && arr[i] <= pivot) {
i++;
}
if (i < j) {
swapItem(arr, i, j);
}
}
swapItem(arr, lo, i);
quickSortInternal(arr, lo, i-1);
quickSortInternal(arr, i+1, hi);
}
private void swapItem(int[] arr, int lo, int hi) {
int tmp = arr[lo];
arr[lo] = arr[hi];
arr[hi] = tmp;
}
2.探索
题中要求是5%,且另有约束为数组长度为20的倍数。K*20*5%=K,岂不美哉,这不是送上门的TOP-K问题嘛,最大、最小可以视为同一个问题,这里拆分为两个TOP-K、MIN-K减少一次数组遍历;
TOP-K问题经典解法之一为大根堆、小根堆,时间复杂度为O(NlogK)。 此处解法写的比较丑陋,没有使用堆,使用数组方式维护TOP-K元素,整体时间复杂度为O(N*N/20*2)。 进一步优化最终可以做到O(N*logN/20*2) ≈ O(NlogN)。
代码-2.探索
class Solution {
public double trimMean(int[] arr) {
// 边界条件
if (arr.length < 20 || arr.length % 20 != 0 || arr.length > 1000) {
return 0L;
}
// 获取TOP-K、MIN-K的结果,在遍历的过程中计算totalSum
double total = 0l;
// 1.预处理K的数值
int kValue = arr.length / 20;
int[][] kArr = new int[2][kValue]; // 记录TOP-K、MIN-K
for (int index = 0; index < kValue; index++) {
kArr[1][index] = -1;
}
// 2.遍历数组
for (int tmp : arr) {
// 计算总和
total += tmp;
// 3.1 TOP-K
int topMinIndex = 0;;
while (topMinIndex < kValue) {
if (kArr[0][topMinIndex] < tmp) {
break;
}
topMinIndex++;
}
// 新的TOP-K元素出现
if (topMinIndex < kValue) {
for (int index = kValue-1; index > topMinIndex; index--) {
kArr[0][index] = kArr[0][index-1];
}
kArr[0][topMinIndex] = tmp;
}
// 3.2 MIN_K
topMinIndex = 0;
while (topMinIndex < kValue) {
if (kArr[1][topMinIndex] == -1 || kArr[1][topMinIndex] > tmp) {
break;
}
topMinIndex++;
}
// 新的MIN-K元素出现
if (topMinIndex < kValue) {
for (int index = kValue-1; index > topMinIndex; index--) {
kArr[1][index] = kArr[1][index-1];
}
kArr[1][topMinIndex] = tmp;
}
}
// 4. 计算最终结果
for (int index = 0 ; index < kValue; index++) {
total -= kArr[0][index];
if (kArr[1][index] != -1) {
total -= kArr[1][index];
}
}
return total / (arr.length - 2*kValue);
}
}
- 时间复杂度:O(NlogN) vs O(NlogN)【原地快排】
- 空间复杂度:O(N) vs O(1)【原地快排】
3.归途
上述结果显示,直接排序(1.初见)和TOP-K(2.探索)求解方式最终时间复杂度基本一致,且原地快排空间复杂度O(1)更优。
从业务场景角度考虑的话,TOP-K的方式可能更加适合,当数组长度不断增长时,只需要维护数组值总和外加对应的大根堆、小根堆;相比而言,快排在数组长度每发生一次变化时,就需要进行一次排序。
不同场景下有不同的解决办法,快排快速代码简单是其优势,实际场景应用若需要用到一些中间状态数组,则需要寻找其他的解法。