数据结构与算法--堆

304 阅读3分钟

Heap

基本性质

数据结构算法之: 堆

Java程序员日常编写代码需要用到根的时候,一般会用JDK中的优先队列PriorityQueue进行代替,特性同堆。本文主要总结堆的原理,即实现的一些细节,以及堆的应用--堆排序的简单用法

堆的特点:

  1. 堆一般指二叉堆
  2. 完全二叉树
  3. 小根堆 | 大根堆

一般用一个数组来存取堆的元素,如:

数组:int[] arr = {7,8,9,12,13,11};

对应的二叉堆如下图所示,:

图片参考自博客,侵删: blog.csdn.net/u013728021/… 二叉堆-heap

根据上图每个节点的idx标号,不难发现如下规律:

若设当前节点的idxi,则可推如下等式:

左孩子

int leftChild(int p) {
        return 2*p+1;
    }

右孩子

int rightChild(int p) {
        return 2*p+2;
    }

双亲节点

int parent(int p) {
        if (p == 0){
            return -1;
        }
        return (p-1)/2;
    }

小根堆

二叉堆可进一步分为:

  1. 小根堆
  2. 大根堆

特点是: 小根堆即双亲节点的值小于左右孩子节点的值;大根堆为双亲节点值大于左右孩子节点的值

本文采用小根堆进行实现,堆的创建一般分两个函数进行实现:

  1. createHeap (创建堆)
  2. heapify (维护堆的性质,小根堆 or 大根堆)

createHeap

void createHeap(int[] arr,int length) {
        int pp = parent(length-1);
        while (pp>=0) {
            heapify(arr,pp,length);
            pp--;
        }
    }

由上代码可以总结步骤为:

  1. 计算第一个非叶子节点pp
  2. 从此节点pp自右向左进行堆结构维护

从第一个非叶子节点开始维护这些节点的堆特性就可以保证整个堆的特性,因为剩下的都是叶子节点,就算值很大,也没有左右孩子和它进行比较了。因此,从第一个非叶子节点进行堆特点维护,如果当前节点不满足特点的话,就把当前节点降到二叉堆的下层即可。

heapity 🌟

void heapify(int[] arr,int root,int len) {
        int rl = leftChild(root);
        int rr = rightChild(root);
        int min = -1;
        if (rl < len && arr[root] > arr[rl]) {
            min = rl;
        }else {
            min = root;
        }
        if (rr < len && arr[min] > arr[rr]) {
            min = rr;
        }
        if (min != root) {
            int temp = arr[min];
            arr[min] = arr[root];
            arr[root] = temp;
            heapify(arr,min,len);
        }
    }

上面的整个过程描述如下:

heapify代码的主要思想就是维护当前节点root的堆特点;即维护最小根的特性,双亲节点的值小于左右孩子的值。因此需找个min变量来存最小当前三个节点的最小值,最后判断是不是root是最小的,如果root是最小的,就不需要再进行调整了。如果不是最小的,就换到最下面,下层存的是大值,然后继续递归调用heapify这个过程。

堆的应用 -- 堆排序

由上述堆的特性可知,当前二叉堆范围内的最小值一定是根节点。这个特性,可以用于排序,具体算法如下:

void heapSort(int[] arr) {
        int len = arr.length;
        createHeap(arr,len);
        //
        for (int i=arr.length-1;i>0;i--) {
            int temp = arr[0];
            arr[0] = arr[i];
            arr[i] = temp;
            heapify(arr,0,i);
        }
    }

heapsort算法的主要步骤是:

  1. 将数组形成一个最小堆heap
  2. 每次将堆的根节点,也就是当前范围的最小值与数组最后一位进行交换。
  3. 然后将数组的有效长度len-1,并从新的根节点开始维护最小堆的特点
  4. 当前的堆顶arr[0]存的是,范围0~len-1内最小的值
  5. 重复step2 ,直到(len-1)==0
  6. 此时arr就是一个逆序数组了,排好序了

heapsort

如下案例所示

public static void main(String[] args) {
        UnUMinHeap minHeap = new UnUMinHeap();
        int[] arr = new int[]{8,3,5,2,4,2,5,7,7,2,3,1};
        minHeap.heapSort(arr);
        System.out.println(Arrays.toString(arr));
        
    }

输出如下: heapSort

参考

  1. www.cnblogs.com/whatyouknow…
  2. blog.csdn.net/u013728021/…
  3. www.cnblogs.com/luomeng/p/1…