1、题目:数组中的第K个最大元素
给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
2、解题思路
方法一:基于快速排序的方法
首先简单介绍一下快速排序:
(1)首先设定一个分界值,通过该分界值将数组分成左右两部分。
(2)将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值。
(3)然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
(4)重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。
从第二步中,我们不难发现,进行一次快速排序的步骤之后,分界值在数组中的位置是可以确定下来的,基于此思想,我们就可以在每次快速排序的过程当中,检测是否找到了题目要求的位置,如果找到了就不需要再进行接下来的排序操作,并且,通过比较分界值与目标值的位置,我们可以只数组的一侧进行排序。
方法二:基于堆排序的方法(此题需要着重掌握的方法)
建立一个大根堆,做 k−1 次删除操作后堆顶元素就是我们要找的答案。在很多语言中,都有优先队列或者堆的的容器可以直接使用,但是在面试中,面试官更倾向于让更面试者自己实现一个堆。所以建议读者掌握这里大根堆的实现方法,在这道题中尤其要搞懂「建堆」、「调整」和「删除」的过程。
从下往上依次进行每个小三角的位置排序
一次排序完成后,删除堆顶元素,即最大值
先将末节点补位
从上往下,从左往右进行排序操作,然后重复删除交换位置
3、代码
//基于快速排序思想
class Solution {
public:
int quickSelect(vector<int>& a, int left, int right, int index)
{
//q为快速排序后当前值的下标值,表示的是此值在容器中的位置
int q = randomPartition(a, left, right);
if (q == index)
{
//如果q==index,那么说明直接找到了那个位置,就将那个位置的值返回
return a[q];
}
else
{
//如果q!=index,说明没有找到正确位置的值
//当q<index时,表示目标值在q值后面,所以只需要对后半部分进行快速排序的操作,反之亦然
return q < index ? quickSelect(a, q + 1, right, index) : quickSelect(a, left, q - 1, index);
}
}
inline int randomPartition(vector<int>& a, int left, int right)
{
//随机定一个在需要快速排序中作为比较值的那个值的位置
int i = rand() % (right - left + 1) + left;
//swap的意思是:快速排序需要将比较值与每个值进行比较,所以把它移到最右边,这样进行下一步排序会比较方便
swap(a[i], a[right]);
return partition(a, left, right);
}
//此函数为快速排序的部分,具体逻辑为,将比较值与每个值进行比较,比比较值小的值和比较值的位置互换
inline int partition(vector<int>& a, int left, int right)
{
int x = a[right], i = left - 1;
for (int j = left; j < right; ++j)
{
if (a[j] <= x)
{
swap(a[++i], a[j]);
}
}
swap(a[i + 1], a[right]);
return i + 1;
}
int findKthLargest(vector<int>& nums, int k)
{
//设置随机数种子,变成真随机
srand(time(0));
return quickSelect(nums, 0, nums.size() - 1, nums.size() - k);
}
};
//堆排序的思想
class Solution
{
public:
//此函数可以让三角的小堆变成最大值在父节点上,反复递归就可以实现堆排序,使堆成为大根堆
void maxHeapify(vector<int>& a, int i, int heapSize)
{
int left = i * 2 + 1, right = i * 2 + 2, largest = i;
if (left < heapSize && a[left] > a[largest])
{
largest = left;
}
if (right < heapSize && a[right] > a[largest])
{
largest = right;
}
if (largest != i)
{
swap(a[i], a[largest]);
maxHeapify(a, largest, heapSize);
}
}
//此函数作用为建立一个最大堆
void buildMaxHeap(vector<int>& a, int heapSize)
{
for (int i = heapSize / 2 - 1; i >= 0; --i)
{
maxHeapify(a, i, heapSize);
}
}
int findKthLargest(vector<int>& nums, int k)
{
int heapSize = nums.size();
buildMaxHeap(nums, heapSize);
for (int i = nums.size() - 1; i >= nums.size() - k + 1; --i)
{
//交换nums[0]和nums[i]并且--heapSize,这样最大值就访问不到了,效果相当于删去最大值
swap(nums[0], nums[i]);
--heapSize;
//删除元素之后再重新进行排序
maxHeapify(nums, 0, heapSize);
}
return nums[0];
}
};