堆呢,就是完全二叉树,分为大根堆和小根堆,大根堆呢,就是每个节点的值大于它左右孩子的值,小根堆与此恰恰相反。
下图是一个典型的大根堆和小根堆:
堆排序,顾名思义,就是利用堆的性质进行排序,那么如何通过构建堆实现有效的排序?
对于一组数,我们首先要做的就是构建一个堆来存储它们,构建的方法便是将数组中的头一个元素视为根节点的元素,将堆节点从左到右,从上到下进行标号,向下面这样:
容易发现对于节点标号为i的元素,其左右孩子的节点的标号分别为i*2+1,i*2+2,,如此便可实现数组与堆的映射。
每次构建一个大根堆,那么数组对应的第一个元素就是最大元素,将数组第一个与第n个元素互换,那么最大元素便被放到了最后面,接着对剩下的n-1个元素继续构建大根堆,重复之前的操作,最终便实现了数组的升序排序。
首先要建堆,相关代码如下:
*/
public static void heapify(int[] arrays, int currentRootNode, int size) {
if (currentRootNode < size) {
//左子树和右字数的位置
int left = 2 * currentRootNode + 1;
int right = 2 * currentRootNode + 2;
//把当前父节点位置看成是最大的
int max = currentRootNode;
if (left < size) {
//如果比当前根元素要大,记录它的位置
if (arrays[max] < arrays[left]) {
max = left;
}
}
if (right < size) {
//如果比当前根元素要大,记录它的位置
if (arrays[max] < arrays[right]) {
max = right;
}
}
//如果最大的不是根元素位置,那么就交换
if (max != currentRootNode) {
int temp = arrays[max];
arrays[max] = arrays[currentRootNode];
arrays[currentRootNode] = temp;
//继续比较,直到完成一次建堆
heapify(arrays, max, size);
}
}
}
public static void maxHeapify(int[] arrays, int size) {
// 从数组的尾部开始,直到第一个元素(角标为0)
for (int i = size - 1; i >= 0; i--) {
heapify(arrays, i, size);
}
}
建堆时应当注意顺序,即应当采取自底向上的构建方法,因为构建更大的堆要求其子堆先满足要求。
for (int i = 0; i < arrays.length; i++) {
//每次建堆就可以排除一个元素了
maxHeapify(arrays, arrays.length - i);
//交换
int temp = arrays[0];
arrays[0] = arrays[(arrays.length - 1) - i];
arrays[(arrays.length - 1) - i] = temp;
}
每次建堆时,都可以排除一个最大元素,因而每次需要排序的数组的长度不断的递减。
如此,利用堆排序,便可以构建出一个有序的数组。
其空间复杂度为O(1),相当于就地排序,时间复杂度为O(nlgn)。