堆结构就是用数组实现的完全二叉树结构

完全二叉树中如果每棵子树的最大值都在顶部就是大跟堆

完全二叉树中如果每棵子树的最小值都在顶部就是小根堆

以0开始

左孩子:2 * i + 1

右孩子:2 * i + 2

父 (i - 1) / 2

另外一种表示:下标为0弃掉     方便使用位运算

左孩子2 * i         》》》》》对应的位运算 i<<1

右孩子2 * i + 1   》》》》》对应的位运算 i<<1 | 1

父 i/2                  》》》》》对应的位运算 i>>1

 

实现:

package com.zy.base.class004;

import java.util.Arrays;

public class MyMaxHeap {
    public static void main(String[] args) {
        MyMaxHeap myMaxHeap = new MyMaxHeap(10);
        myMaxHeap.push(1);
        myMaxHeap.push(9);
        myMaxHeap.push(8);
        myMaxHeap.push(10);
        System.out.println(myMaxHeap);
        myMaxHeap.pop();
        System.out.println(myMaxHeap);
    }

    private int[] heap;
    private int limit;
    private int heapSize;

    public MyMaxHeap(int limit) {
        this.heap = new int[limit];
        this.limit = limit;
        this.heapSize = 0;
    }

    public boolean isEmpty() {
        return heapSize == 0;
    }

    public boolean isFull() {
        return heapSize == limit;
    }

    public void push(int value) {
        if (this.isFull()) {
            throw new RuntimeException("heap is full");
        }
        heap[heapSize] = value;
        heapInsert(heap, heapSize++);
    }

    // 用户此时,让你返回最大值,并且在大根堆中,把最大值删掉
    // 剩下的数,依然保持大根堆组织
    public int pop() {
        int res = heap[0];
        swap(heap, 0, --heapSize);
        heapify(heap, 0, heapSize);
        return res;
    }

    // 从index位置,往下看,不断的下沉,
    // 停:我的孩子都不再比我大;已经没孩子了
    private void heapify(int[] arr, int index, int heapSize) {
        int left = index * 2 + 1;
        while (left < heapSize) {
            // 左右两个孩子中,谁大,谁把自己的下标给largest
            // 右  ->  1) 有右孩子   && 2)右孩子的值比左孩子大才行
            // 否则,左
            int largest = left + 1 < heapSize && arr[left + 1] > arr[left] ? left + 1 : left;
            largest = arr[largest] > arr[index] ? largest : index;
            if (largest == index) {
                break;
            }
            swap(arr, index, largest);
            index = largest;
            left = index * 2 + 1;
        }

    }

    private void heapInsert(int[] arr, int index) {
        // arr[index]
        // arr[index] 不比 arr[index父]大了 , 停
        // index = 0;
        while (arr[index] > arr[(index - 1) / 2]) {
            swap(arr, index, (index - 1) / 2);
            index = (index - 1) / 2;
        }
    }

    private void swap(int[] arr, int index1, int index2) {
        int tmp = arr[index1];
        arr[index1] = arr[index2];
        arr[index2] = tmp;
    }

    @Override
    public String toString() {
        return "MyMaxHeap{" +
                "heap=" + Arrays.toString(heap) +
                ", limit=" + limit +
                ", heapSize=" + heapSize +
                '}';
    }
}

MyMaxHeap{heap=[10, 9, 8, 1, 0, 0, 0, 0, 0, 0], limit=10, heapSize=4}
MyMaxHeap{heap=[9, 1, 8, 10, 0, 0, 0, 0, 0, 0], limit=10, heapSize=3}

堆排序实现

如果一个一个数字传输

package com.zy.base.class004;

import java.util.Arrays;

public class HeapSort {
    public static void main(String[] args) {
        int[] arr = {5, 1, 10, 6, 2, 4, 8, 3, 9};

        new HeapSort().heapSort(arr);
        System.out.println(Arrays.toString(arr));
    }


    // 堆排序额外空间复杂度O(1)
    public void heapSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        int len = arr.length;
        MyMaxHeap myMaxHeap = new MyMaxHeap(len);
        // O(N*logN)
        for (int i = 0; i < arr.length; i++) { // O(N)
            myMaxHeap.heapInsert(arr, i); // O(logN)
        }

        int heapSize = arr.length;
        myMaxHeap.swap(arr, 0, --heapSize);
        // O(N*logN)
        while (heapSize > 0) { // O(N)
            myMaxHeap.heapify(arr, 0, heapSize); // O(logN)
            myMaxHeap.swap(arr, 0, --heapSize); // O(1)
        }
    }

}

如果是直接给了一个数组,有很多数组,那么可以优化:

package com.zy.base.class004;

import java.util.Arrays;

public class HeapSortOptimization {
    public static void main(String[] args) {
        int[] arr = {5, 1, 10, 6, 2, 4, 8, 3, 9};

        new HeapSortOptimization().heapSort(arr);
        System.out.println(Arrays.toString(arr));
    }

    // 堆排序额外空间复杂度O(1)
    public void heapSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }

        int len = arr.length;
        MyMaxHeap myMaxHeap = new MyMaxHeap(len);
        // O(N*logN)
        // 从最后一个开始,整理数组,调整为大顶堆
        for (int i = len - 1; i >= 0 ; i--) {
            myMaxHeap.heapify(arr, i, len);
        }

        int heapSize = len;
        myMaxHeap.swap(arr, 0, --heapSize);
        // O(N*logN)
        // 然后不断的将最大值调整到最后一个元素,然后调整大顶堆,始终保持着大顶堆的特征
        while (heapSize > 0) { // O(N)
            myMaxHeap.heapify(arr, 0, heapSize); // O(logN)
            myMaxHeap.swap(arr, 0, --heapSize); // O(1)
        }

    }
}

堆排序总结:

1.先让整个数组都变成大根堆结构,建立堆的过程:

    1)从上到下的方法,时间复杂度为O(N*logN)

    2)从下到上的方法,时间复杂度为O(N)

2.把堆的最大值和堆末尾的值交换,然后减少堆的大小之后,再去调整堆,一直周而复始,时间复杂度为O(N*logN)

3.堆的大小减小成0之后,排序完成

与堆有关的题目

已知一个几乎有序的数组。几乎有序是指,如果把数组排好序的话,每个元素移动的距离一定不超过k,并且k相对于数组长度来书是比较小的。

请选择一个合适的排序策略,对这个数组进行排序。

package com.zy.base.class004;

import java.util.Arrays;
import java.util.PriorityQueue;

public class SortArrayDistanceLessK {
    public static void main(String[] args) {
        int maxSize = 100;
        int maxValue = 100;
        int[] arr = randomArrayNoMoveMoreK(maxSize, maxValue, 5);
        System.out.println(Arrays.toString(arr));
        sortedArrDistanceLessK(arr, 5);
        System.out.println(Arrays.toString(arr));
    }

    public static void sortedArrDistanceLessK(int[] arr, int k) {
        if (k == 0) {
            return;
        }

        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();

        int index = 0;
        for (; index <= Math.min(k, arr.length - 1); index++) {
            priorityQueue.add(arr[index]);
        }

        int i = 0;
        for (; index < arr.length; index++) {
            priorityQueue.add(arr[index]);
            arr[i++] = priorityQueue.poll();
        }

        while (!priorityQueue.isEmpty()) {
            arr[i++] = priorityQueue.poll();
        }

    }

    // for test
    public static int[] randomArrayNoMoveMoreK(int maxSize, int maxValue, int K) {
        int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
        }
        // 先排个序
        Arrays.sort(arr);
        // 然后开始随意交换,但是保证每个数距离不超过K
        // swap[i] == true, 表示i位置已经参与过交换
        // swap[i] == false, 表示i位置没有参与过交换
        boolean[] isSwap = new boolean[arr.length];
        for (int i = 0; i < arr.length; i++) {
            int j = Math.min(i + (int) (Math.random() * (K + 1)), arr.length - 1);
            if (!isSwap[i] && !isSwap[j]) {
                isSwap[i] = true;
                isSwap[j] = true;
                int tmp = arr[i];
                arr[i] = arr[j];
                arr[j] = tmp;
            }
        }
        return arr;
    }
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值