LeetCode_215 数组中的第K个最大元素

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];
	}
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值