堆排序使用了“堆”数据结构来进行信息管理,因此,首先需要介绍一个数据结构“堆”。
堆
堆也叫二叉堆,是一个数组,可以被看作一个近似的完全二叉树,树上的每一个节点对应数组中的一个元素。除了最底层外,该树实2完全充满的,而且是从左向右填充。表示堆的对象A应有两个属性,A.data为数组,数组用于存储堆的数据,A.heapSize表示A.data[0,A.heapSize-1]中存放的是堆的有效元素。A.data的第一个元素为树的根节点。若下标从1开始,可以知道给定一个节点的下标i,该节点的父节点下标为i/2,该节点的左孩子下标为2i,右孩子下标为2i+1。但是往往在编写代码实现时,数组下标从0开始,此时,节点i的父节点下标为(i-1)/2,左孩子下标为2xi+1,右孩子下标为2x(i+1) 。
下图分别为一个堆的二叉树和数组展现形式。
二叉堆可以分为最大堆和最小堆。最大堆满足A.data[parent[i]] ≥ A.data[i],最小堆满足A.data[parent[i]] ≤ A.data[i]。
堆排序
基于堆实现排序,可通过以下方法实现:
- MAX-HEAPIFY:此过程维护最大堆的性质,保证堆是一颗最大堆,时间复杂度为O(lgn);
- BUILD-MAX-HEAP:此过程从无序数组中构建一个最大堆,具有线性时间复杂度;
- HEAPSORT:对一个数组进行原址排序,时间复杂度为O(nlgn)。
java实现
- 定义堆的数据结构
/**
* 堆结构
*/
public class Heap {
/**
* 堆的大小
*/
private int heapSize;
/**
* 存储堆的数组
*/
private int[] heapDate;
public int getHeapSize() {
return heapSize;
}
public void setHeapSize(int heapSize) {
this.heapSize = heapSize;
}
public int[] getHeapDate() {
return heapDate;
}
public void setHeapDate(int[] heapDate) {
this.heapDate = heapDate;
}
}
- 实现MAX-HEAPIFY方法
/**
* 维护最大堆性质,时间复杂度为O(lgn)
*
* @param heap 最大堆
* @param i 维护堆的起始索引,从1开始
*/
private void maxHeapIfy(Heap heap, int i) {
// 左孩子下标
int left = 2 * i + 1;
// 右孩子下标
int right = 2 * (i + 1);
int largest;
int[] heapNums = heap.getHeapDate();
// 每棵子树的孩子节点小于等于其父节点
if (left <= heap.getHeapSize() - 1 && heapNums[left] > heapNums[i]) {
largest = left;
} else {
largest = i;
}
if (right < heap.getHeapSize() - 1 && heapNums[right] > heapNums[largest]) {
largest = right;
}
if (largest != i) {
int temp = heapNums[i];
heapNums[i] = heapNums[largest];
heapNums[largest] = temp;
maxHeapIfy(heap, largest);
}
}
- 实现BUILD-MAX-HEAP方法
/**
* 建堆
*
* @param nums 用来构建堆的数组
* @return 建好的堆
*/
private Heap buildHeap(int[] nums) {
Heap heap = new Heap();
heap.setHeapSize(nums.length);
heap.setHeapDate(nums);
for (int i = nums.length / 2; i >= 0; i--) {
maxHeapIfy(heap, i);
}
return heap;
}
- 实现HEAPSORT方法
/**
* 堆排序
*
* @param nums 待排序的数组
*/
public void heapSort(int[] nums) {
// 建堆
Heap heap = buildHeap(nums);
int[] heapDate = heap.getHeapDate();
// 从数组的最后一个元素开始,每次将堆的根节点与最后一个元素互换
for (int i = heapDate.length - 1; i >= 0; i--) {
int temp = nums[0];
nums[0] = nums[i];
nums[i] = temp;
// 将堆的大小减一,再次进行调整,使其满足堆的性质
heap.setHeapSize(heap.getHeapSize() - 1);
maxHeapIfy(heap, 0);
}
}