在数组中找到第k大的元素
注意事项
你可以交换数组中的元素的位置
给出数组 [9,3,2,4,8]
,第三大的元素是 4
给出数组 [1,2,3,4,5]
,第一大的元素是 5
,第二大的元素是 4
,第三大的元素是 3
,以此类推
要求时间复杂度为O(n),空间复杂度为O(1)
快速排序算法的主要思想为以下几步:
(1)首先选中一个基准数;
(2)在数组中进行排序,使得比基准数小的全在他左边,比基准数大的全在他右边;
(3)对上面得到的左右区间分别执行(2)中操作,直到最终区间的数字只有一个为止。
下面对快速排序算法进行详细的解释:有如下所示的数组
0 | 1 | 2 | 3 | 4 | 5 | 6 |
3 | 2 | 5 | 1 | 8 | 0 | 3 |
假定我们选择数组的第0个元素作为基准数X且按从小到大的顺序进行排序,有初始时i = 0 ,j = 6,X = nums[i]
首先判断nums[j] >= X吗,如果成立,则不动他同时有j--寻找下一个,如果不成立,则nums[i] = nums[j];同时i++
然后判断nums[i] < X吗,如果成立,则不动他,同时有i++寻找下一个,如果不成立,则nums[j] = nums[i];同时j--
这样操作直到i>=j为止,此时数组中比X小的在他左边,比他大的在他右边。
然后分别对得到的左右区间如此操作,直到最后区间的大小为1为止。
具体的快速排序的代码如下所示(从小到大排序):
int quickSort(vector<int> &numList, int left, int right)
{
if (left < right)//判断left到right的这个区间是否已经排序完成
{
//对left到right的这个区间进行排序
int i = left;
int j = right;
int tmp = numList[left];
while (i < j)//当i>=j时,排序完成
{
while (i < j && tmp <= numList[j])//从数组末尾开始找,直到找到比基准数tmp小的数
{
j--;
}
if (i < j)//判断前面的循环跳出是因为没有找到还是找到了比tmp小的数
{
numList[i++] = numList[j];//若找到了
}
while (i<j && tmp > numList[i])//从数组开头开始找,直到找到比基准数tmp大的数
{
i++;
}
if (i < j)//判断前面的循环跳出是因为没有找到还是找到了比tmp大的数
{
numList[j--] = numList[i];//若找到了
}
}
numList[i] = tmp;
quickSort(numList, left, i - 1);
quickSort(numList, i + 1, right);
}
}
说完了快速排序,那么如何解决这道题呢
对数组利用快速排序从大到小排序的思想,找到每一次快速排序时基准数的下标(基准数的下标即为排序好后该数的真实下标),直到找到下标为k-1的数为止。(具体怎么算时间复杂度我也不太清楚,还要学习,网上说这种算法的时间复杂度为O(n))
具体代码如下:
int quickSort(vector<int> &numList, int left, int right)
{
//对left到right的这个区间进行排序
int i = left;
int j = right;
int tmp = numList[left];
while (i < j)//当i>=j时,排序完成
{
while (i < j && tmp >= numList[j])//注意与前面不一样
{
j--;
}
if (i < j)
{
numList[i++] = numList[j];
}
while (i<j && tmp < numList[i])//注意与前面不同
{
i++;
}
if (i < j)
{
numList[j--] = numList[i];
}
}
numList[i] = tmp;
return i;//返回基准数下标
}
int kthLargestElement(int k, vector<int> nums) {
// write your code here
if (nums.size() == 0)
return 0;
int left = 0;
int right = nums.size() - 1;
while (1)
{
int pos = quickSort(nums, left, right);
if (pos == k - 1)//若找到的基准数下标为k-1,即为结果
{
return nums[k - 1];
}
if (pos > k - 1)//若找到的基准数下标比k-1大,则从其左区间中继续寻找,丢弃其右区间
{
right = pos - 1;
}
if (pos < k - 1)//若找到的基准数下标比k-1小,则从其右区间中继续寻找,丢弃其左区间
{
left = pos + 1;
}
}
}