求数组的第K小数,O(nlogn) 和 O(N)的算法

在面试中碰到求数组中第K小的数,(或者最小的的K个数)。


最直观的方法是排序之后,选择数组A的元素A[K-1];  以快速排序为例,排序的时间复杂度为O(NlogN), 选择元素的时间为O(1)。

如果允许使用额外空间,则排序算法可以使使用时间复杂度为O(N)的基数排序。


如果时间复杂度要O(N), 不使用额外空间的话,可以借用快速排序中partition函数来达到想要的结果。

思路是:

在经过partion之后,会得到一个位置(索引)position, 该位置左边的元素都小于position位置的值,右边的都大。

1, 当 postion == K -1时, 该位置元素为所求

2, 当postion < K - 1时,说明应该去postion的右边去找

        3, 否则,去左边找


下边来一个栗子:

求自定类型元素序列的中位数 

本题要求实现一个函数,求N个集合元素A[]的中位数,即序列中第\lceil N/2 \rceilN/2大的元素。其中集合元素的类型为自定义的ElementType

函数接口定义:

ElementType Median( ElementType A[], int N );

其中给定集合元素存放在数组A[]中,正整数N是数组元素个数。该函数须返回NA[]元素的中位数,其值也必须是ElementType类型。

裁判测试程序样例:

#include <stdio.h>

#define MAXN 10
typedef float ElementType;

ElementType Median( ElementType A[], int N );

int main ()
{
    ElementType A[MAXN];
    int N, i;

    scanf("%d", &N);
    for ( i=0; i<N; i++ )
        scanf("%f", &A[i]);
    printf("%.2f\n", Median(A, N));

    return 0;
}

/* 你的代码将被嵌在这里 */

输入样例:

3
12.3 34 -5

输出样例:

12.30

解答:

注意事项: 根据题目对中位数的定义, 在数据量为偶数的时候, 我们需要的是中间两个数中右边的那一个,而不是两个元素的中指


代码:

#include <stdio.h>

#define MAXN 10
typedef float ElementType;

ElementType Median(ElementType A[], int N);

int main(){
	ElementType A[MAXN];
	int N, i;
	scanf("%d", &N);
	for(i=0; i<N; i++)
		scanf("%f",&A[i]);
	printf("%.2f\n", Median(A,N));
	// printf("Test:\n");
	// for(int i = 0; i < N; ++i){
	// 	printf("%f ", A[i]);
	// }
	return 0;
}

void swap(ElementType A[], int i, int j){
	// printf("Swaped! ");
	ElementType temp = A[i];
	A[i] = A[j];
	A[j] = temp;
}

int partition(ElementType A[], int N, int left, int right){
	// printf("reached! %d %d ", left, right);
	ElementType pivot = A[right];
	int index = left - 1;
	for(int i = left; i < right; ++i){
		if(A[i] < pivot) swap(A, ++index, i);
	}
	swap(A, ++index, right);
	return index;
}

// 查找N个元素中的第K个小的元素(来自编程珠玑)
ElementType Median(ElementType A[], int N){
	// printf("reached! ");
	int position = -1, left = 0, right = N - 1;
	int target = N / 2;
	// if(N % 2 == 0) --target; 
	// if(position == target) return A[target];
	while(1){
		if (position < target) {
			left = position + 1;
			position = partition(A, N, left, right);
		}
		else if(position > target){
			right = position - 1;
			position = partition(A, N, left, right);
		}
		else {
			// printf("position: %d ", position);
			return A[position];
			// break;
			// return position;
		}	
	}
}











  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值