1. 程式人生 > >排序演算法整理(6)堆排序的應用,top K 問題

排序演算法整理(6)堆排序的應用,top K 問題

top K問題是這樣的,給定一組任意順序的數,假設有n個。如何儘快地找到它們的前K個最大的數?

首先,既然是找前K個最大的數,那麼最直觀的辦法是,n個數全部都排序,然後挑出前K個最大數。但是這樣顯然做了一些不必要的事兒。

利用堆這種資料結構,藉助前文《排序演算法整理(5)堆排序》中談到的維護堆的函式, min_heapify( ),就可以輕鬆解決top K問題。

主要步驟如下:

step 1. 隨意選出K個數,挑出這K個數的最小的數。這個過程可以用最小堆完成。

step 2. 在剩下的n – K個數中,挑出任意一個數m,和最小堆的堆頂進行比較,如果比最小堆的堆頂大,那麼說明此數可以入圍前K的隊伍,於是將最小堆的堆頂置為當前的數m。

step 3. 調整最小堆。時間複雜度為Olg(K),由於K是constant(常數級別),所以時間複雜度可以認為是常數級別。

step 4. 重複進行step 2 ~ step 3,直到剩下的n – K個數完成。進行了n –constant次,時間複雜度為O(n lgK).

核心程式碼如下:

void top_k(int * p_arr, int length,int k, int * p_res)
{		
	int * adjusted_array =  new int[k+1];
	adjusted_array[0]=0;
	for(int i=0;i<k;i++) //構建前k名構成的堆。
		adjusted_array[i+1]=p_arr[i];
	build_heap_min(adjusted_array,k);
	for(int j=k;j<length;j++)
	{
		if(adjusted_array[1]<p_arr[j])
		{
			adjusted_array[1]=p_arr[j];
			min_heapify_recur(adjusted_array,1,k);
		}
	}	
	for(int m=1;m<=k;m++)	
		p_res[m-1]=adjusted_array[m];		
	delete [] adjusted_array;//拷貝給p_res後再刪除adjusted_array	
	return;	
}

完整實現程式碼如下。有heap.h和heap.cpp和main.cpp這3個檔案。

這是heap.h

//heap.h檔案
#include <stdio.h>
#include <stdlib.h>
inline int l_child(int i);
inline int r_child(int i);
inline int parent(int i);
static void max_heapify_recur(int * p_arr,int i,int heap_size);
static void max_heapify_norecur(int * p_arr,int i,int heap_size);
static void min_heapify_recur(int * p_arr,int i,int heap_size);
void build_heap_max(int * p_arr,int heapsize);
void build_heap_min(int * p_arr,int heapsize);
void heap_sort_max(int * p_arr, int length);
void heap_sort_min(int * p_arr, int length);
void top_k(int * p_arr, int length, int k, int * p_res);

這是heap.cpp
//heap.cpp檔案
#include "heap.h"

inline int l_child(int i)
{
	return 2*i;
}

inline int r_child(int i)
{
	return 2*i+1;
}
inline int parent(int i)
{
	return i/2;
}

static void max_heapify_recur(int * p_arr,int i,int heap_size) 
{
	//if(i>heap_size/2)//葉節點不需要進行堆建立
	//	return;
	int l = l_child(i);
	int r = r_child(i);
	int largest = 0;
	if ( (l<= heap_size) && (p_arr[l]>p_arr[i]) )
	    largest = l;
	else
		largest = i;
	if( (r<=heap_size) && (p_arr[r]>p_arr[largest]) )
		largest = r;
//	printf("i:%d,largest:%d,array[i]:%d,array[largest]:%d\n",i,largest,p_arr[i],p_arr[largest]);
	if (largest != i) {
		int temp = p_arr[i];
	    p_arr[i] = p_arr[largest];
		p_arr[largest] = temp;
		max_heapify_recur(p_arr, largest,heap_size);
	}
	return;
}


static void max_heapify_norecur(int * p_arr,int i,int heap_size) 
{
	while(i<=heap_size/2) {	
		int l = l_child(i);
		int r = r_child(i);
		int largest = 0;
		if ( (l<= heap_size) && (p_arr[l]>p_arr[i]) )
			largest = l;
		else
			largest = i;
		if( (r<=heap_size) && (p_arr[r]>p_arr[largest]) )
			largest = r;
//		printf("i:%d,largest:%d,array[i]:%d,array[largest]:%d\n",i,largest,p_arr[i],p_arr[largest]);
		if (largest != i) {
			int temp = p_arr[i];
			p_arr[i] = p_arr[largest];
			p_arr[largest] = temp;		
			i=largest;
		}
	}
	return;
}



static void min_heapify_recur(int * p_arr,int i,int heap_size)
{
	//if(i>heap_size/2)//葉節點不需要進行堆建立
	//	return;
	int l = l_child(i);
	int r = r_child(i);
	int smallest = 0;
	if ( (l<= heap_size) && (p_arr[l]<p_arr[i]) )
	    smallest = l;
	else
		smallest = i;
	if( (r<=heap_size) && (p_arr[r]<p_arr[smallest]) )
		smallest = r;
//	printf("i:%d,largest:%d,array[i]:%d,array[largest]:%d\n",i,largest,p_arr[i],p_arr[largest]);
	if (smallest != i) {
		int temp = p_arr[i];
	    p_arr[i] = p_arr[smallest];
		p_arr[smallest] = temp;
		min_heapify_recur(p_arr, smallest, heap_size);
	}
	return;
}

void build_heap_max(int * p_arr,int heapsize)
{
	int start=heapsize/2;
	for(int i = start;i>=1;i-- )
	{
		max_heapify_recur(p_arr,i,heapsize);	
	}
	return;
}

void build_heap_min(int * p_arr,int heapsize)
{
	int start=heapsize/2;
	for(int i = start;i>=1;i-- )
	{
		min_heapify_recur(p_arr,i,heapsize);	
	}
	return;
}

void heap_sort_max(int * p_arr, int length)
{
	int * adjusted_array =  new int[length+1];
	adjusted_array[0]=0;
	for(int i=0;i<length;i++) //構建下表從1開始的陣列作為adjusted_array,方便進行堆排序
		adjusted_array[i+1]=p_arr[i];
	
	build_heap_max(adjusted_array,length);
	for(int heap_size=length;heap_size>=1;heap_size--) {		
		int temp = adjusted_array[1];
		adjusted_array[1] = adjusted_array[heap_size];
		adjusted_array[heap_size] = temp;	    
		max_heapify_recur(adjusted_array,1,heap_size-1);
	}

	for(int i=0;i<length;i++) //將adjusted_array還原為輸入陣列	
		p_arr[i]=adjusted_array[i+1];
	delete [] adjusted_array;
	return;
}


void heap_sort_min(int * p_arr, int length)
{
	int * adjusted_array =  new int[length+1];
	adjusted_array[0]=0;
	for(int i=0;i<length;i++) //構建下表從1開始的陣列作為adjusted_array,方便進行堆排序
		adjusted_array[i+1]=p_arr[i];

    build_heap_min(adjusted_array,length);

	for(int heap_size=length;heap_size>=1;heap_size--) {		
		int temp = adjusted_array[1];
		adjusted_array[1] = adjusted_array[heap_size];
		adjusted_array[heap_size] = temp;	    
		min_heapify_recur(adjusted_array,1,heap_size-1);		
	}

	for(int i=0;i<length;i++) //將adjusted_array還原為輸入陣列(也是輸出陣列)p_arr[]
		p_arr[i]=adjusted_array[i+1];
	delete [] adjusted_array;
	return;
}




void top_k(int * p_arr, int length,int k, int * p_res)
{		
	int * adjusted_array =  new int[k+1];
	adjusted_array[0]=0;
	for(int i=0;i<k;i++) //構建前k名構成的堆。
		adjusted_array[i+1]=p_arr[i];
	build_heap_min(adjusted_array,k);
	for(int j=k;j<length;j++)
	{
		if(adjusted_array[1]<p_arr[j])
		{
			adjusted_array[1]=p_arr[j];
			min_heapify_recur(adjusted_array,1,k);
		}
	}
	
	for(int m=1;m<=k;m++)	
		p_res[m-1]=adjusted_array[m];		
        
	delete [] adjusted_array;//拷貝給p_res後再刪除adjusted_array	
	return;	
}

這是main.cpp
//main.cpp檔案
#include "heap.h"

//int src_arr[11] = {-1,1,2,3,4,5,6,7,8,9,10};
//int src_arr[21] = {-1,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
//int src_arr[10] = {1,2,3,4,5,6,7,8,9,10};
//int src_arr[10] = {10,9,8,7,6,5,4,3,2,1};
int src_arr[10] = {8,4,6,3,5,2,1,7,9,10};
int src_arr_top_k[10] = {8,4,6,3,5,2,1,7,9,10};

void print_arr(int *p_arr, int length) 
{
	printf("array is:");
	for(int i =1;i<=length;i++) //為了方便,這裡跳過p_arr[0],相當於陣列下標從1開始計數。
		printf("\t%d",p_arr[i]);
	printf("\n");
}

void print_arr_2(int *p_arr, int length) 
{
	printf("array is:");
	for(int i =0;i<length;i++) //為了方便,這裡認為陣列下標從0開始計數。
		printf("\t%d",p_arr[i]);
	printf("\n");
}

int main()
{
	
	//top K
	int top_k_number = 6;
	int * p_res = new int[top_k_number];	
	int heap_size2 = sizeof(src_arr_top_k)/sizeof(int);
        printf("Top k is :%d\n",top_k_number);	
        printf("Before top k:\n");
	print_arr_2(src_arr_top_k,top_k_number);
	top_k(src_arr_top_k, heap_size2,top_k_number, p_res );
	printf("After top k:\n");
	print_arr_2(p_res,top_k_number);	
	delete [] p_res;

	int a =  getchar();
	return 0;
}
輸出結果如下