5. 数组中的第K个最大元素 + 荷兰国旗问题【LC215】

197 阅读3分钟

题目: 给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

示例 1:

输入: [3,2,1,5,6,4] 和 k = 2 输出: 5 示例 2:

输入: [3,2,3,1,2,4,5,5,6] 和 k = 4 输出: 4

普通解:

// sort()倒序排序
var findKthLargest = function(nums, k) {
    return nums.sort((a,b) => b-a)[k - 1]
};


sort() 方法用于对数组的元素进行排序。

排序顺序可以是字母或数字,并按升序或降序。

默认排序顺序为按字母升序。
若 a 小于 b,在排序后的数组中 a 应该出现在 b 之前,则返回一个小于 0 的值。// 比较的是排序后的
若 a 等于b,则返回 0。
若 a 大于 b,则返回一个大于 0 的值。
简单点就是:比较函数两个参数ab,返回a-b 升序,返回b-a 降序

问题在于用什么排序复杂度更低。以及剪枝,比如排到什么情况下就不用再担心处理了。 分析题目,最简单的方式就是将整个数组进行排序后输出第K大元素,但是使用排序算法后最好平均时间复杂度也是 O(nlog⁡n),但是我们可以通过快排的思想进行优化,使平均时间复杂度变为O(n)。

根据快排partition思想,进行快速选择,时间复杂度能达到O(n)。

快速选择(荷兰国旗问题)
取TopK类型问题;

给定一个数组arr,和一个数num,请把小于等于num的数放在数 组的左边,大于num的数放在数组的右边;(荷兰国旗问题)

TopK类型问题:(荷兰国旗问题思路)

先说下Partition

image.png


function partition(arr, l, r) {
    let pivot = arr[r] // 枢轴
    let less = l - 1; // 维护小于枢轴的区域
    let more = r; // 维护大于枢轴的区域;
    // l相当于等于区域右侧指针,即当前位指针
    // l指针右移,用于维护等于枢轴的区域
    while(l < more) {
        if(arr[l] < pivot) {
            swap(arr, ++less, l++) //  小的数值要换到小数值区域指针后一位,也就是小数值区域扩展,l指针右移,处理下一项。
        } else if(arr[l] > pivot) {
            swap(arr, l, --more) // l指针停留,more指针前移
        } else {
            l++;
        }
    }
    swap(arr, l, r) // l指针指向more指针指向相同
    return [less+1, more] // 等于 pivot的数组索引
}

再说一下快排partition实现思路

function quickSort(arr, left, right) {
    if(left < right) {
        let pivot = partition(arr, left, right);
        quickSort(arr, left, pivot[0] - 1);
        quickSort(arr, pivot[1] + 1, right);
    }
}

利用快排partition思路,取TopK

function findKthLargest(arr, k) {
    const len = nums.length;
    const targetIndex = len - k;
    let left = 0, right = len - 1;
    while(left < right) {
        const index = partition(arr, left, right);
        const [s, e] = index || [];
        if(s <= targetIndex && targetIndex <= e) {
            return arr[targetIndex]
        } else if(targetIndex < s) {
            left = s - 1;  // 一点一点扩
        } else {
            right = e + 1;  // 一点一点扩
        }
    }
    
}

引申问题: 给定一个数组arr,和一个数num,请把小于等于num的数放在数 组的左边,大于num的数放在数组的右边;

分析题干,不在意排序问题。
将小于等于某个值的item全部放到左边,大于的放到右边

需要一个X指针,来维护一个小于区域即可

代码实现:
var x = -1;
     for ( var i=0; i<arr.length; i++){
         arr[i] <= num ? arr[++x]交换arr[i] : null;
}