LintCode笔记(12)——第k大元素

在数组中找到第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 &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 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 &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 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;
		}
	}
}



你可能感兴趣的:(LintCode)