当前位置:编程学习 > C/C++ >>

用堆排序寻找数组中最大的K个数

[cpp] 
/***********************************************************************************
堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。
堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:
即子结点的键值或索引总是小于(或者大于)它的父节点。
通常堆是通过一维数组来实现的。在起始数组为 0 的情形中:
父节点i的左子节点在位置 (2*i+1);
父节点i的右子节点在位置 (2*i+2);
子节点i的父节点在位置 floor((i-1)/2);
堆的操作
在堆的数据结构中,堆中的最大值总是位于根节点。堆中定义以下几种操作:
最大堆调整(Min_Heapify):将堆的末端子结点作调整,使得子结点永远小于父结点
创建最大堆(Build_Min_Heap):将堆所有数据重新排序
注:堆排序不是一种稳定排序。
 
用小根堆得办法寻找最大的K个数
用容量为K的最小堆来存储最大的K个数。最小堆的堆顶元素就是最大K个数中的最小的一个。
每次扫描一个数据X,如果X比堆顶元素Y小,则不需要改变原来的堆。如果X比堆顶元素大,
那么用X替换堆顶元素Y,在替换之后,X可能破坏了最小堆的结构,需要调整堆来维持堆的性质。
调整过程时间复杂度为O(logK)。 全部的时间复杂度为O(N*logK)。
这种方法当数据量比较大的时候,比较方便。因为对所有的数据只会遍历一次,
*************************************************************************************/ 
 
#include <cmath>  
#include<cstdlib>  
#include<time.h>  
#include<cstdio>  
#include<iostream>  
using namespace std; 
//产生随机数组  
void Random(int a[],int n) 

    int i=0; 
    srand( (unsigned)time( NULL ) ); 
    while(i<n) 
    { 
        a[i++]=rand()/999; 
    } 

void print(int a[], int len) 

    int i; 
    for (i = 0; i < len; i++) 
    printf("%6d   ", a[i]); 
    printf("\n"); 

 
 
int parent(int); 
int left(int); 
int right(int); 
void Min_Heapify(int [], int, int); 
void Build_Min_Heap(int A[],int size); 
void HeapSort(int [], int); 
 
/*求父亲*/ 
int parent(int i) 

    return (int)floor((i - 1) / 2); 

 
/*求左孩子*/ 
int left(int i) 

    return (2 * i + 1); 

 
/*求右孩子*/ 
int right(int i) 

    return (2 * i + 2); 

 
/*调整堆使其满足堆得性质*/ 
void Min_Heapify(int A[], int i, int heap_size) 

    int l = left(i); 
    int r = right(i); 
    int least; 
    int temp; 
    /*找到父亲 左孩子 右孩子 之间的最大值*/ 
    if(l < heap_size && A[l] < A[i]) 
    { 
        least = l; 
    } 
    else 
    { 
        least = i; 
    } 
    if(r < heap_size && A[r] < A[least]) 
    { 
        least = r; 
    } 
    /*如果父亲不是最大的,则把父亲和两个孩子的较大值交换*/ 
    if(least != i) 
    { 
        temp = A[i]; 
        A[i] = A[least]; 
        A[least] = temp; 
        /*交换之后破坏了较大孩子的堆得性质,对其进行调整*/ 
        Min_Heapify(A, least, heap_size); 
    } 

 
/*简历大顶堆*/ 
void Build_Min_Heap(int A[],int size) 

    /* 因为数组A[0]要存放数据 所以左孩子为2*i+1 右孩子为 2*i+2 */ 
    /*取最后一个飞叶子节点   即堆顶  根节点
    0 1 2 ->mid=1=3/2    n/2
    0 1 2 3 ->mid=2=4/2     n/2
    0 1 2 3 4 ->mid=2=5/2   n/2
    0 1 2 3 4 5 ->mid=3=6/2 n/2  取中间元素偏右的为根节点使其成为完全二叉树
    */ 
    int begin = size/2 ;  // 堆顶元素  
    for(int i = begin; i >= 0; i--) 
    { 
        Min_Heapify(A, i, size); 
    } 

 
 
/*堆排序开始*/ 
void HeapSort(int A[], int heap_size) 

    Build_Min_Heap(A,heap_size); 
    //建立大顶堆之后 堆顶已经是所有元素中最大的了  
    int temp; 
    //a[0]是数组的第一个元素 a[heap_size - 1]是数组的最后一个元素  
    /*将堆顶依次和最后一个叶子节点交换*/ 
    for(int i = heap_size - 1; i >= 0; i--) 
    { 
        temp = A[0]; 
        A[0] = A[i]; 
        A[i] = temp; 
        //交换之后破坏了堆得性质 重新调整  
        Min_Heapify(A, 0, i);  //i 是元素个数  每交换依次元素就少一个  
    } 

 
void TopK(int arr[],int n,int K) 

    if(n<K) 
    { 
        cout<<"error"<<endl; 
        return; 
    } 
    int *heap=new int[K]; 
    //随机将前K个数装入数组构建小顶堆  
    for(int i=0;i<K;i++) 
    { 
        heap[i]=arr[i]; 
    } 
    Build_Min_Heap(heap,K);//建立最小堆  
    //从生下的数中找比小顶堆堆顶大的数并与堆顶交换(直接放在堆顶位置不交换效率更高)  
    for(int i=K;i<n;i++) 
    { 
        if(arr[i]>heap[0]) 
        { 
            heap[0]=arr[i]; 
            //破坏了最小对的性质 对最小对进行调整  
            Min_Heapify(heap,0,K); 
        } 
    } 
 

补充:软件开发 , C++ ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,