无序数组中找第K大的数

类快排算法

leetcode215

由于只要求找出第k大的数,没必要将数组中所有值都排序。

典型解法:快速排序分组。

在数组中找到第k大的元素
取基准元素,将元素分为两个集合,一个集合元素比基准小,另一个比基准大 ,三种情况。
1.比基准大的元素数目标志位m正好为k-1,基准就是目的元素。
2.比基准大的元素标志位m小于k, 那么就在比基准小的集合里面找第(k-m)大的元素
3.若是比基准大的元素为m大于k,那就继续在该集合里面找第k大的元素。

快排中的partition算法,返回key在数组中的位置的cnt(相对于left的偏移量),如果cnt正好等于k,那么问题则得到解决;如果cnt小于k,去左边找第k个;如果cnt>k,则去右边找第k-cnt个。直到key的位置等于k-1,则找对问题的解。

/*快排中的划分算法*/
int partition(int* input, int low, int high)
 {
    int tmp = input[low]; // 取一个基准元素

    while (low < high) {
        while (low < high && input[high] <= tmp) {
            high--;
        }
        input[low] = input[high];

        while (low < high && input[low] >= tmp) {
            low++;
        }
        input[high] = input[low];
    }

    input[low] = tmp;
    return low;
}

// 参数left需要小于right
int findK(int* array, int left, int right, int k) {
    //printf("%d %d %d\n", left, right, k);
    int i = partition(array, left, right);
    int cnt = i - left + 1;
    if (k == cnt) {
        return array[i];
    } else if (k < cnt) {
        return findK(array, left, i - 1, k);
    } else if (k > cnt) {
        return findK(array, i + 1, right, k-cnt);
    }
    return 0;
}

此解法的时间复杂度为O(N*lgK),logK次每次O(N),优于排序解法.

测试程序:

#include <iostream>
#include <vector>
using namespace std;

/*快排中的划分算法*/
int partition(int* input, int low, int high)
{
	int tmp = input[low]; // 取一个基准元素

	while (low < high) {
		while (low < high && input[high] <= tmp) {
			high--;
		}
		input[low] = input[high];

		while (low < high && input[low] >= tmp) {
			low++;
		}
		input[high] = input[low];
	}

	input[low] = tmp;
	return low;
}

// 这里得到的是第k小,自己n-k转换下即可, 或者改下划分函数
int findK(int* array, int left, int right, int k) {
	//printf("%d %d %d\n", left, right, k);
	int i = partition(array, left, right);
	int cnt = i - left + 1;
	if (k == cnt) {
		return array[i];
	}
	else if (k < cnt) {
		return findK(array, left, i - 1, k);
	}
	else if (k > cnt) {
		return findK(array, i + 1, right, k - cnt);
	}
	return 0;
}


int main()
{
	vector<int> ilist = {9, 5, 8, 7, 3, 6, 2, 1, 4, 0};
	int arr[10];
	int n = ilist.size();
	std::copy(ilist.begin(), ilist.end(), arr);
	int k = 3;
	int num = findK(arr, 0, n-1, k);
	cout << num << endl;
	system("pause");
	return 0;
}

快排加二分查找:

int a[]={7,9,8,5,6,3,2,4,1,0};
const int K=6;
//解题思路就是利用快速排序,如果得到快速排序一次中间结果之后其所在位置即在
int QSort(int* a,int low,int high)
{
	if(low<high)
	{
		int start=low,end=high;
		int key=a[start];
		while(start<end)
		{
			while(start<end&&a[end]<key)
			{
				end--;
			}
			a[start]=a[end];
			while(start<end&&a[start]>key)
			{
				start++;
			}
			a[end]=a[start];			
		}
		a[start]=key;
		return start;
	}
	return -1;
}
int main()
{
	int start=0,end=sizeof(a)/sizeof(int)-1;
	int index=-2;
	while(index+1!=K)
	{
		index=QSort(a,start,end);
		if(index<K)
		{
			start=index+1;
		}
		else if(index>K)
		{
			end=index-1;			
		}
	}
	cout<<a[index]<<"即为所求\n";
}

 

最小堆解法

构造一个大小为k的最小堆,堆中根节点为最小值。如果数组中最大的几个数均在堆中,那么堆中根节点的值就是问题的解。

可以用STL中的优先队列实现,因为优先队列的内部就是最大堆/最小堆实现的。

此解法的时间复杂度O(NlogK),N次logK。但是相比与类快速排序算法,需要额外的存储空间。

#include <iostream>
#include <vector>
using namespace std;

void maxHeapify(int* array, int size, int i) {
	int left = 2 * i + 1;
	int right = 2 * i + 2;
	int small = i;
	if (left < size) {
		if (array[small] > array[left]) {
			small = left;
		}
	}
	if (right < size) {
		if (array[small] > array[right]) {
			small = right;
		}
	}
	if (small != i) {
		int temp = array[small];
		array[small] = array[i];
		array[i] = temp;
		maxHeapify(array, size, small);
	}
}

void buildHeap(int* array, int size) {
	for (int i = size - 1; i >= 0; i--) {
		maxHeapify(array, size, i);
	}
}

int findKByHeap(int* array, int n, int k) {
	buildHeap(array, k);
	for (int i = k; i < n; i++) {
		if (array[i] > array[0]) {
			int temp = array[i];
			array[i] = array[0];
			array[0] = temp;
			maxHeapify(array, k, 0);
		}
	}
	return array[0];
}


int main()
{
	vector<int> ilist = {9, 5, 8, 7, 3, 6, 2, 1, 4, 0};
	int arr[10];
	int n = ilist.size();
	std::copy(ilist.begin(), ilist.end(), arr);
	int k = 5;
	int num = findKByHeap(arr, n, k);
	cout << num << endl;	

	system("pause");
	return 0;
}

参考博客:寻找第k大的数

其他可学习参考:[LeetCode]215 数组第k大的数

在无序数组中找到第k大的数

【算法】在N个乱序数字中查找第K大的数字

如何寻找无序数组中的第K大元素?

从无序数组中找出第K大的数

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
可以使用快速选择算法来解决这个问题,其时间复杂度为O(N)。 快速选择算法基于快速排序,但不需要完全排序整个数组。它通过每次选择一个基准元素并将数组分为两个部分来工作,然后根据基准元素的位置来确定我们需要搜索的那个部分。 具体地,我们可以首先随机选择一个元素作为基准元素,然后将数组中比该元素小的元素放在它的左边,比该元素大的元素放在它的右边。假设基准元素的位置为p,则左边的子数组中有p个元素小于它,右边的子数组中有n-p-1个元素大于它。如果k=p,则基准元素即为第k小的元素;如果k<p,则我们需要在左边的子数组中继续搜索第k小的元素;如果k>p,则我们需要在右边的子数组中继续搜索第k-p-1小的元素。 下面是使用快速选择算法解第k小的的C代码: ``` #include <stdio.h> #include <stdlib.h> int partition(int* nums, int left, int right) { int pivot = nums[left]; int i = left + 1, j = right; while (i <= j) { while (i <= j && nums[i] < pivot) { i++; } while (i <= j && nums[j] >= pivot) { j--; } if (i < j) { int temp = nums[i]; nums[i] = nums[j]; nums[j] = temp; } } int temp = nums[left]; nums[left] = nums[j]; nums[j] = temp; return j; } int quickSelect(int* nums, int left, int right, int k) { if (left == right) { return nums[left]; } int pivotIndex = partition(nums, left, right); if (k == pivotIndex) { return nums[k]; } else if (k < pivotIndex) { return quickSelect(nums, left, pivotIndex - 1, k); } else { return quickSelect(nums, pivotIndex + 1, right, k); } } int findKthSmallest(int* nums, int size, int k) { return quickSelect(nums, 0, size - 1, k - 1); } int main() { int nums[] = {3, 2, 1, 5, 6, 4}; int size = sizeof(nums) / sizeof(int); int k = 2; int kthSmallest = findKthSmallest(nums, size, k); printf("The %dth smallest element is %d\n", k, kthSmallest); return 0; } ``` 在这个例子中,我们使用数组{3, 2, 1, 5, 6, 4}来演示如何到第2小的元素。我们首先调用findKthSmallest函,并将nums数组的大小和k值作为参传递。在findKthSmallest函中,我们调用quickSelect函来实际查第k小的元素。在quickSelect函中,我们首先将left和right参传递给partition函,以便将数组分为两个部分。然后,我们根据pivotIndex的值决定搜索哪个子数组。如果k等于pivotIndex,则我们到了第k小的元素;否则,我们递归调用quickSelect函来搜索左边或右边的子数组。最后,我们在main函中打印出到的第k小的元素。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落花逐流水

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值