堆的概念及其在排序和topK问题中的应用

堆的概念及其在排序和TopK问题中的应用

堆(heap)是一种特殊的数据结构,其形象化是一个特殊的完全二叉树(CBT),满足任意节点始终不大于(或不小于)左右子节点。若该二叉树的根的值为最小值,称为小顶堆,反之则称为大顶堆。

          1                        1                        1             
        /   \                    /   \                    /   \
       2     3                  4     5                  3     2     
      /       \                / \                      / \   / 
     4         5              2   3                    6   5 4    
         (1)                      (2)                      (3)
      not a CBT           a CBT but not heap           a mintop heap   

然而堆的另一个特殊之处在于,通常用数组去存储堆,而不是二叉树。上图(3)中的小顶堆可以用[1,3,2,6,5,4]来表示。将任意一个数组转化为堆数组的操作称为堆化。

堆化

大顶堆

public static void buildMaxHeap(int[] arr){
        int length = arr.length;
        for(int i=length/2-1; i>=0; i--){
            adjustMaxHeap(arr, i, length);
        }
    }

    public static void adjustMaxHeap(int[] arr, int i, int length){
        int temp = arr[i];
        for(int k=2*i+1; k<length; k = k*2+1){
            if(k+1<length && arr[k]<arr[k+1]){
                k++;
            }
            if(arr[k] > temp){
                arr[i] = arr[k];
                i = k;
            }else{
                break;
            }
        }
        arr[i] = temp;
    }

小顶堆

public static void buildMinHeap(int[] arr){
        int length = arr.length;
        for(int i=length/2-1; i>=0; i--){
            adjustMinHeap(arr, i, length);
        }
    }

    public static void adjustMinHeap(int[] arr, int i, int length){
        int temp = arr[i];
        for(int k=2*i+1; k<length; k=k*2+1){
            if(k+1<length && arr[k]>arr[k+1]){
                k++;
            }
            if(arr[k] < temp){
                arr[i] = arr[k];
                i = k;
            }else{
                break;
            }
        }
        arr[i] = temp;
    }

堆排序

堆可以应用于排序算法中,若想得到升序,则建立大顶堆,反之则建立小顶堆。本文以升序为例,首先将待排序数组建立成一个大顶堆,此时,整个数组中最大值为堆顶的值,即arr[0],然后将arr[0]与末尾元素进行交换。然后将剩余的前n-1个元素重新构造成一个大顶堆,再将arr[0]与第n-1个元素进行交换。如此反复,即可对数组进行排序。代码如下

public static void sort(int[] arr){
    buildMaxHeap(arr);
    for(int i=arr.length-1; i>0; i--){
        swap(arr, 0,i);
        adjustMaxHeap(arr, 0, i);
    }
}
public static void swap(int[] arr, int a, int b){
    int temp = arr[a];
    arr[a] = arr[b];
    arr[b] = temp;
}

TopK问题

问题描述:有 N个数,求出其中的前K个最大(小)的数。
这类问题如果求前K个最大的数,需要用到小顶堆,求前K个最小的数,需要用到大顶堆。本文以取前K个最大的数为例,首先取N个数中前K个数,建立一个大小为K的小顶堆。堆顶为最小的数,然后对N个数中剩余的N-K个数进行遍历,若原数组中的元素小于堆顶的元素,则将堆顶进行替换并调整堆。该算法的复杂度为O(N*logK),代码如下

public static int[] topK(int[] arr,int k){
        int[] ans = Arrays.copyOfRange(arr, 0, k);
        buildMinHeap(ans);
        for(int i=k; i<arr.length; i++){
            if(ans[0]<arr[i]){
                ans[0] = arr[i];
                adjustHeap(arr, 0, k);
            }
        }
        return ans;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值