[jobdu]最小的K个数

一开始马上想起来寻找第k小的数,是采用快排的partition方法。但因为题目要把k之前的数排序输出,这个方法就不是很合适,因为(随机化后:http://blog.csdn.net/liangbopirates/article/details/9377105)它最差能在O(n)找到第k小的数,那么就要最差k*O(n)。如果对前面部分排序那就是O(k*logk) + O(n)了。都不合适,看来最合适的还是使用堆,能做到O(n*logK)。

但partition方法和堆方法比较,有个好处就是inplace,不占多余内存,在海量数据处理的时候有这个好处。

下面这段代码是寻找第k小的数,其精华部分是partition方法,注意这个方法的写法,维基百科和算法导论上都是这样的,比一般的写法简洁,需要记下来。本质是分了三段,第一段小于等于pivot,第二段大于pivot,第三段是未处理的。idx一直指向第二段的第一个,或者说第一段最右边过去一个,i一直指向第二段的最右边,或是第三段的最左边。那么当a[i]<pivot的时候,就把a[i]和a[idx]交换,然后idx++,这样就继续保持了这个性质。

#include <iostream>
using namespace std;

void swap(int& a, int& b)
{
	int tmp = a;
	a = b;
	b = tmp;
}

// start、end分别为数组第一个元素和最后一个元素的索引
int partition(int arr[], int start, int end)
{
    int pivot = arr[end];
    int idx = start;
    //这个循环比一般的写法简洁高效,维基百科上和算法导论上的
    for(int i = start; i < end; i++) {
        if(arr[i] < pivot) {
            swap(arr[i], arr[idx]);
            ++idx;
        }
    }
    swap(arr[idx], arr[end]);
    return idx;
}

int main()
{
	int n; // count
	cin >> n;
	int m; // index
	cin >> m;
	int* a = new int[n];
	for (int i = 0; i < n; i++)
	{
		cin >> a[i];
	}
	int start = 0;
	int end = n - 1;
	while (true)
	{
		int c = partition(a, start, end);
		if (c == (m-1))
		{
			break;
		}
		if (c > (m - 1))
		{
			end = c - 1;
		}
		else
		{
			start = c + 1;
		}
	}
	cout << a[m-1] << endl;
	delete[] a;
	system("pause");
}

下面是采取堆的方式,(待续):

posted @ 2013-08-31 14:54  阿牧遥  阅读(281)  评论(0编辑  收藏  举报