Leetcode 215 算法 - 求数组中第K个最大元素 - 快速排序
这是力扣上一个中等难度的题目,题目是这样的
给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。 => 也就是说,对整个整数数组从大到小进行排序,数组下标为k-1的那个元素,就是我们需要的元素
[3,2,1,5,6,4], k = 2 => 数组中第1大的是6,第二大的是5,所以返回5
[3,2,3,1,2,4,5,5,6], k = 4 => 数组中第1大的是6,第2大的是5,第3大的是另一个5,第4大的是4, 所以返回4
所以,我们第一时间想到的解法就是对数组进行降序排序,排序完成后的数组中下标为k-1的那个元素,就是我们的答案
这道题如果不是考算法,只是平常的一道题,那么我们直接用数组内置sort方法很快就能解决,比如
public class solution { public int GetMaxKElement(int[] arr, int k) { Array.Sort(arr); // 对数组从小到大排序 return arr[arr.Length - k]; } }
但是考算法,就需要我们自己写,我们用快速排序来实现,快速排序可以参考我之前写的一个算法 https://www.cnblogs.com/wphl-27/p/23114374
快速排序的核心主要是包括2点,一是 哨兵划分,二是递归
哨兵划分 - 以数组中某个元素(一般选第一个或最后一个元素)为基准,将所有小于该基准数的元素移动到其左边,所有大于该基准数的元素移动到其右边.
递归 - 对左子数组和右子数组递归执行哨兵划分,直到子数组长度为1时终止递归,从而完成整个数组的快速排序
我们来看一下数组 [2,4,1,0,3,5] 的快速排序流程

我们在快速排序的那个博客(https://www.cnblogs.com/wphl-27/p/23114374)中提到,时间复杂度是O(NLogN). 但是如果数组中包含大量的重复元素,每轮的哨兵划分都可能将数组划分了1和N-1两部分,这种情况下,快速排序的时间复杂度可能退化为O(N*N) => 这里的解决方案是使用 【三路划分】, 也就是说每轮将数组划分了3个部分: 小于,等于和大于基准数的所有元素. => 我们用C++中的vector来实现
#include <vector> using std::vector; class solution { public: int FindKLarget(vector<int>& nums, int k) { return quickSelect(nums, k); } private: int quickSelect(vector<int>& nums, int k) { //选择基准数 int pivot = nums.front(); // 将数组分为大于基准数、小于基准数和等于基准数的三部分 vector<int> big, small, equal; for(int num : nums) { if(num > pivot) big.push_back(num); else if(num < pivot) small.push_back(num); else equal.push_back(num); } // 3部分数组划分好后,判断第K个元素落在哪部分,从而对该部分进行递归 //比如big的size是5,K是4,那么原数组的第4大的元素,肯定会在big里面,因为big里面是原数组中前5个大的元素 if(k <= big.size()) return quickSelect(big, k); //k在small的size里面,那就说明big和equal的长度加起来都比K小(也就是原数组长度减去big长度),所以第K大的元素一定在small里面 else if(k > nums.size() - big.size()) return quickSelect(small,k); else //第K大的元素在equal数组里面,直接返回 return pivot; } };
浙公网安备 33010602011771号