排序算法千千万,今天来讲堆排序!!
关于「详解快排及其应用」,可见 详解快排及其应用
关于「详解归并排序及其应用」,可见 详解归并排序及其应用
「快排」和「归并」都是一次性将所有元素排好顺序,但有一些场景,我们每次可能只需要获得最小值即可
「堆排序」就是这样的特点,并非一次性全部排好序,而只有堆顶元素是最小值,每次需要获取最小值时,只需要去堆顶取即可
借鉴 Java 中的优先队列,我们的堆只实现几个核心的 API
xxxxxxxxxxpublic MyPQ(); // 默认构造函数public MyPQ(int c); // 带初始容量的构造函数public void offer(int x); // 插入一个元素 xpublic int poll(); // 弹出堆顶元素public int peek(); // 获取堆顶元素public int size(); // 获取堆的大小定义:当一棵二叉树的每个结点都大于等于它的两个子结点时,它被称为堆有序
根据定义可知,根结点是堆有序的二叉树中的最大结点
定义:二叉堆是一组能够用堆有序的完全二叉树排序的元素,并在数组中按照层级储存 (不使用数组的第一个位置)
根据定义可知,在一个堆中,位置
我们用长度为N + 1的私有数组pq[]来表示一个大小为N的堆,我们不会使用pq[0],堆元素放在pq[1]至pq[N]中
注意:二叉堆可以分为大根堆和小根堆,其中大根堆就是上面定义中介绍的,而小根堆是每个节点都小于等于它的两个子节点,即:根结点是最小结点。本篇文章基于「小根堆」来实现!!!
每次插入一个新的元素时,首先将该元素放在数组的最后一个空位置,然后执行由下至上的堆有序化操作,即「上浮」
假设一个有序堆,如下图所示:
此时,插入一个新的元素4,如下图所示:
然后执行由下至上的堆有序化操作,如下图所示:
由下至上的堆有序化 (上浮) 实现代码如下:
xxxxxxxxxx// k 是需要上浮的元素下标private void up(int k) { // 未到根节点,且当前元素小于其父节点 while (k > 1 && pq[k] < pq[k / 2]) { // 交换 swap(k, k / 2); // k 上浮 k >>= 1; }}每次删除堆顶元素时,首先将该元素与最后一个元素交换,然后将最后一个元素删除,最后执行由上至下的堆有序化操作,即「下沉」
在上面插入元素后的基础上删除堆顶元素,下图所示:
然后执行由上至下的堆有序化操作,如下图所示:
由上至下的堆有序化 (下沉) 实现代码如下:
xxxxxxxxxx// k 是需要下沉的元素下标private void down(int k) { // 未到最后一个元素 while (2 * k <= size) { // 左孩子下标 int j = 2 * k; // 选出左右孩子中最小的一个 if (j < size && pq[j] > pq[j + 1]) j++; // 如果左右孩子中最小的还比当前节点大,则 break if (pq[k] < pq[j]) break; // 否则交换 swap(k, j); // k 下沉 k = j; }}核心的两个操作介绍完了,下面给出完整的代码模版:(小根堆)
xxxxxxxxxxclass MyPQ { private static final int CAPACITY = 11; private int[] pq; private int size; public MyPQ() { pq = new int[CAPACITY]; size = 0; } public MyPQ(int c) { pq = new int[c + 1]; size = 0; } public void offer(int x) { // 加到末尾,size + 1 pq[++size] = x; // 上浮最后一个元素 up(size); } public int poll() { // 记录堆顶元素 int x = pq[1]; // 交换 swap(1, size--); // 下沉第一个元素 down(1); return x; } public int peek() { return pq[1]; } public int size() { return size; } private void up(int k) { while (k > 1 && pq[k] < pq[k / 2]) { swap(k, k / 2); k >>= 1; } } private void down(int k) { while (2 * k <= size) { int j = 2 * k; if (j < size && pq[j] > pq[j + 1]) j++; if (pq[k] < pq[j]) break; swap(k, j); k = j; } } private void swap(int i, int j) { int t = pq[i]; pq[i] = pq[j]; pq[j] = t; }}