几种查找数组的前K个最小值的算法

来源:本站
导读:目前正在解读《几种查找数组的前K个最小值的算法》的相关信息,《几种查找数组的前K个最小值的算法》是由用户自行发布的知识型内容!下面请观看由(电工技术网 - www.9ddd.net)用户发布《几种查找数组的前K个最小值的算法》的详细说明。
简介:好久没有写博客了,这一段时间主要在准备为将来找工作复习,今天我就总结一下关于如何查找数组的前K个最小值实现方法,查找前K个最小值实现方法很多。

主要的思想包括如下的几种

1、对数组进行排序,然后前K个元素就是需要查找的元素,排序的方法可以采用快速排序,但是我们知道在快速排序中如果已经是有序的数组,采用快速排序的时间复杂度是O(N^2),为了解决这种问题,通常选择随机选择一个数组值pivot作为基准,将数组分为S1 =< pivot和S2 > pivot,这样就能避免快速排序中存在的问题,或者采用随机选择三个元素,然后取中间值作为基准就能避免快速算法的最差时间复杂度,这种方法的前K个数字是有序的。

2、既然是选择前K个对象,那么就没必要对所有的对象进行排序,可以采用快速选择的思想获得前K个对象,比如首先采用快速排序的集合划分方法划分集合:S1,pivot,S2,然后比较K是否小于S1的个数,如何小于,则直接对S1进行快速排序,如果K的个数超过S1,那么对S2进行快速排序,排序完成之后,取数组的前K个元素就是数组的前K个最小值。这种实现方法肯定比第一种的全快速排序要更快速。

3、将数组转换为最小堆的情况,根据最小堆的特性,第一个元素肯定就是数组中的最小值,这时候我们可以将元素保存起来,然后将最后一个元素提升到第一个元素,重新构建最小堆,这样进行K次的最小堆创建,就找到了前K个最小值,这是运用了最小堆的特性,实质上是最小堆的删除实现方法。这种算法的好处是实现了数组的原地排序,并不需要额外的内存空间。

4、接下来的这种思想有点类似桶排序,首先给定一个K个大小的数组b,然后复制数组a中的前K个数到数组b中,将这K个数当成数组a的前K个最小值,对数组b创建最大堆,这时候再次比较数组a中的其他元素,如果其他元素小于数组b的最大值(堆顶),则将堆顶的值进行替换,并重新创建最大堆。这样遍历一次数组就找到了前K个最小元素。这种方法运用了额外的内存空间,特别当选择的K值比较大时,这种方法有待于权衡一下。

这种方法对于海量数据来说是有较好的作用,对于海量数据不能全部存放在内存中,这时候创建一个较小的数组空间,然后创建最大堆,从硬盘中读取其他的数据,进而实现前K个数据的查找。

这是比较传统的几种方法,当然还存在其他的选择方式,我在这边就不阐述了,从上面几种方法的可知,查找方法都充分运用了运用了数据结构和算法的特性。因此数据结构的灵活运用对算法的实现有很多的好处。

下面是我的实现代码,数组中前K个元素我通过打印的方式实现,并没有保存到新的数组中:

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

#include<assert.h>

#include<time.h>

#define LEN 500000

#define K 100

/*堆的性质*/

#define LEFTSON(i) (2*(i)+1)

#define RIGHTSON(i) (2*((i)+1))

#define PARENT(i) (((i)-1)/2)

void swap(int *a, int *b)

{

assert(a != NULL && b != NULL);

if(a != b)

{

*a = *a ^ *b;

*b = *a ^ *b;

*a = *a ^ *b;

}

}

int partition(int *a, int left, int right)

{

int pivot = a[right];

int i = left;

int j = left - 1;

assert(a != NULL);

for(i = left; i < right; ++ i)

{

if(a[i] < pivot)

{

++ j;

swap(&a[i],&a[j]);

}

}

swap(&a[j + 1],&a[right]);

return (j + 1);

}

void quicksort(int *a, int left, int right)

{

int i = 0;

assert(a != NULL);

if(left < right)

{

i = partition(a,left,right);

quicksort(a, left, i - 1);

quicksort(a, i + 1, right);

}

}

int QuickSort(int *a, int size)

{

assert(a != NULL);

quicksort(a,0,size-1);

}

void quickselect(int *a, int left, int right, int k)

{

int i = 0;

assert(a != NULL && left <= k

&& left <= right && k <= right);

if(left < right)

{

i = partition(a, left, right);

if(i + 1 <= k)

quickselect(a, i + 1 , right, k);

else if(i > k)

quickselect(a, left, i - 1, k);

}

}

void QuickSelect(int *a, int size, int k)

{

assert(a != NULL);

quickselect(a, 0, size - 1, k);

}

/*最大堆*/

void max_heapify(int *a, int left, int right)

{

int tmp = 0;

int child = left;

int parent = left;

assert(a != NULL);

for(tmp = a[parent]; LEFTSON(parent) <= right;parent = child)

{

child = LEFTSON(parent);

if(child != right && a[child] < a[child + 1])

child ++;

if(tmp < a[child])

a[parent] = a[child];

else /*满足最大堆的特性,直接退出*/

break;

}

a[parent] = tmp;

}

/*创建最大堆*/

void build_maxheap(int *a, int size)

{

int i = 0;

assert(a != NULL);

for(i = PARENT(size); i >= 0 ; -- i)

max_heapify(a,i,size - 1);

}

/*最小堆的实现*/

void min_heapify(int *a, int left, int right)

{

int child = 0;

int tmp = 0;

int parent = left;

assert(a != NULL);

for(tmp = a[parent]; LEFTSON(parent) <= right; parent = child)

{

child = LEFTSON(parent);

if(child != parent && a[child] > a[child + 1])

child ++;

if(a[child] < tmp)

a[parent] = a[child];

else /*满足最小堆的特性,直接退出*/

break;

}

a[parent] = tmp;

}

/*创建最小堆*/

void build_minheap(int *a, int size)

{

int i = PARENT(size);

assert(a != NULL);

for(; i >= 0; -- i)

min_heapify(a, i, size - 1);

}

/*采用快速排序查找*/

void find_Kmin_num_1(int *a , int size, int k)

{

int i = 0;

assert(a != NULL);

QuickSort(a, size);

#if 0

for(i = 0; i < k ; ++ i)

printf("%dt",a[i]);

printf("n");

#endif

}

/*采用快速选择实现*/

void find_Kmin_num_2(int *a, int size, int k)

{

int i = 0;

assert(a != NULL);

QuickSelect(a, size, k);

#if 0

for(i = 0; i < k ; ++ i)

printf("%dt",a[i]);

printf("n");

#endif

}

/*采用最大堆实现*/

void find_Kmin_num_3(int *a, int size, int k)

{

int i = 0;

int *b = malloc(sizeof(int)*k);

assert(a != NULL && b != NULL);

for(i = 0; i < k; ++ i)

b[i] = a[i];

build_maxheap(b,k);

for(; i < size; ++ i)

{

if(a[i] < b[0])

{

b[0] = a[i];

// build_maxheap(b , k);

max_heapify(b,0,k - 1);

}

}

#if 0

for(i = 0; i < k ; ++ i)

printf("%dt",b[i]);

printf("n");

#endif

}

/*采用最小堆删除元素的方式实现*/

void find_Kmin_num_4(int *a ,int size, int k)

{

int i = 0;

assert(a != NULL);

build_minheap(a, size - 1);

for(i = 0; i < k; ++ i)

{

// printf("%dt",a[0]);

/*删除a[0],释放a[size - 1 - i]*/

a[0] = a[size -1 - i];

min_heapify(a, 0, size - 2 - i);

}

// printf("n");

}

int main()

{

int a[LEN];

int b[LEN];

int c[LEN];

int d[LEN];

int i = 0,j = 0;

clock_t _start;

double times = 0;

srand((int)time(NULL));

for(i = 0; i < LEN; ++ i)

{

a[i] = rand()%(LEN);

b[i] = a[i];

c[i] = a[i];

d[i] = a[i];

// printf("%dt",a[i]);

}

// printf("n");

_start = clock();

find_Kmin_num_1(a,LEN,K);

times = (double)(clock() - _start)/CLOCKS_PER_SEC;

printf("快速排序的查找需要:%fn",times);

_start = clock();

find_Kmin_num_2(b,LEN,K);

times = (double)(clock() - _start)/CLOCKS_PER_SEC;

printf("快速选择的查找需要:%fn",times);

_start = clock();

find_Kmin_num_3(c,LEN,K);

times = (double)(clock() - _start)/CLOCKS_PER_SEC;

printf("最大堆的查找需要:%fn",times);

_start = clock();

find_Kmin_num_4(d,LEN,K);

times = (double)(clock() - _start)/CLOCKS_PER_SEC;

printf("最小堆的查找需要:%fn",times);

return 0;

}

检测算法的性能:

[gong@Gong-Computer interview]$ gcc -g minKnum.c -o minKnum

[gong@Gong-Computer interview]$ ./minKnum

快速排序的查找需要:0.130000

快速选择的查找需要:0.020000

最大堆的查找需要:0.000000

最小堆的查找需要:0.010000

从结果可知,快速排序的算法效果最差,而最大堆的效果最好,最小堆的效果其次,但是最大堆运用了额外的内存空间。因此在内存空间限制的情况下,考虑最小堆是比较合适的。但是最大堆的思想确实很精妙的,运用了类似桶排序的性质。

为了说明算法能否实现前K个最小值的查找,改变数组大小为50,并打印各个方法完成的情况,查找前10个数据,实验结果如下所示:

[gong@Gong-Computer interview]$ ./minKnum

15 38 14 43 31 45 42 1 32 23 43 34 9 4 45 31 25 48 8 42 40 27 36 30 32 4 11 23 47 12 24 14 1 40 8 32 36 0 35 18 26 28 2 35 35 49 17 12 48 27

0 1 1 2 4 4 8 8 9 11

快速排序的查找需要:0.000000

1 9 4 8 4 11 1 8 0 2

快速选择的查找需要:0.000000

11 8 9 4 2 1 8 1 4 0

最大堆的查找需要:0.000000

0 1 1 2 4 4 8 8 9 11

最小堆的查找需要:0.000000

从上面的实验结果可知,四种方法都是实现了获得前K个最小元素。

提醒:《几种查找数组的前K个最小值的算法》最后刷新时间 2024-03-14 01:02:08,本站为公益型个人网站,仅供个人学习和记录信息,不进行任何商业性质的盈利。如果内容、图片资源失效或内容涉及侵权,请反馈至,我们会及时处理。本站只保证内容的可读性,无法保证真实性,《几种查找数组的前K个最小值的算法》该内容的真实性请自行鉴别。