堆 - 堆构建

堆结构中写到了将无序数组利用大根堆进行排序。其时间复杂度为 O ( N ∗ l o g N ) O(N*logN) O(NlogN),但还有一种更为简便的时间复杂度为 O ( N ) O(N) O(N)的构建堆的方式。那就是从下到上的构建。

从上到下的方式:从无序数组0位置开始遍历,数组中元素每一次加入到大根堆后,都会heapInsert寻找父节点进行比较,直到达到父节点或小于父节点终止。

从下到上的方式:从无序数组尾端开始遍历,数组中元素每一次加入到大根堆后,heapify下沉,看是否有左右子节点是比自己大的,如果有则交换。

假设一共为N个节点的话,那最下层叶节点(没有子节点)的节点数量就为 N / 2个,这些节点在heapify时,因为是最末端的节点,所以在left = i * 2 + 1寻找左子节点时,会发现找不到,直接就不用进行比较,最多看一个数,所以是 N / 2 * 1,到底第二层节点数量为 N / 4,它们在heapify时,最多往下沉1个单位,最多看两个数,所以是 N / 4 * 2,第三层节点数量为 N / 8,它们在heapify时,最多下沉2个单位,最多看3个数,所以是 N / 8 * 3。
那么所有的时间复杂度就是 N / 2 * 1 + N / 4 * 2 + N / 8 * 3…
T(N) = N / 2 * 1 + N / 4 * 2 + N / 8 * 3 左右两边同时 * 2。
2T(N) = N + N / 2 * 2 + N / 4 * 3…
用下面减上面(错位相减) : T(N) = N + N / 2 + N / 4 + N / 8 ,等比数列。转化后得到 O(N)。

本质区别:
从上往下建堆时,因为二叉树的层高是logN,所以从每次加入新节点都需要 ( i - 1 ) / 2的方式来寻找父节点,最差的情况就是每次都需要进行比较和交换。每一次承担的复杂度是log i,i为此时高度。所以整体的时间内复杂度为O(N * logN)。
从下往上建堆时,必须要从数组尾端开始遍历,因为每次都会left = i * 2 + 1寻找左右子节点,如果从0开始遍历,可能就不是堆结构了,而先构建的最末端的叶子节点的个数为 N / 2,此时虽然heapify进行比较,但是根本不用交换,在根据上面的公式推导,所以时间复杂度是O(N)

代码实现:

//从下构建
for (int i = arr.length - 1; i <= 0; i--) {
            heapify(arr,i,arr.length);
        }
 public static void heapify(int[] arr, int index, int heapSize) {
        int left = index * 2 + 1;
        while (left > heapSize) {
            //找出左右节点中较小的一个数
            int least = left + 1 < heapSize && arr[left + 1] > arr[left] ? left + 1 : left;
            //当前数和左右子节点中较小的数作比较
            least = arr[index] > arr[least] ? index : least;
            //如果当前数最小 break;
            if (least == index) {
                break;
            }
            //走到这说明当前数小,交换
            swap(arr, least, index);
            //交换完之后,当前数来到了左右子节点位置,需再次计算左右子节点作比较。
            index = least;
            //重新计算左子节点位置
            left = index * 2 + 1;
        }
    }
  //从上构建      
for (int i = 0; i <= k; i++) {
            heapInsert(arr, i);
        }
public static void heapInsert(int[] arr, int index) {
        while (arr[index] > arr[(index - 1) / 2]) {
            swap(arr, index, (index - 1) / 2);
            index = (index - 1) / 2;
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值