堆排序算法步骤
- 把无序数组构建成二叉堆。需要从小到大排序的,构建大根堆。需要从大到小的则构建小根堆。
- 循环删除堆顶元素,替换到二叉堆的末尾。调整产生新的堆顶。
过程示例
如上图所示,在删除值为10的堆顶节点后,经过调整,值为9的新节点就会顶替上来;在删除值为9的堆顶节点后,经过调整,值为8的新节点就会顶替上来…
由于二叉堆的这个特性,每一次删除旧堆顶,调整后的新堆顶都是大小仅次于旧堆顶的节点。那么只要反复删除堆顶,反复调整二叉堆,所得到的集合就会成为一个有序集合,过程如下。
删除节点9,节点8成为新堆顶。
删除节点8,节点7成为新堆顶。
删除节点7,节点6成为新堆顶。
删除节点6,节点5成为新堆顶。
删除节点5,节点4成为新堆顶。
删除节点4,节点3成为新堆顶。
删除节点3,节点2成为新堆顶。
到此为止,原本的最大二叉堆已经变成了一个从小到大的有序集合。二叉堆实际存储在数组中,数组中的元素排列如下。
性能
- 稳定性:不稳定
- 时间复杂度:O(nlogn)
代码实现
* 下沉调整
* @param array 待调整数组
* @param parentIndex 要下沉的父节点
* @param length 堆的有效大小
*/
public static void downAdjust(int[] array, int parentIndex,int length) {
//保存父节点的值,用于最后赋值
int temp = array[parentIndex];
int childIndex = 2 * parentIndex + 1;
while (childIndex<length) {
//如果有右孩子,且右孩子大于左孩子的值,则定位到右孩子
if(childIndex + 1 < length && array[childIndex+1] > array[childIndex]) {
childIndex++;
}
//如果父节点大于任何一个孩子的值,则直接跳出
if(temp >= array[childIndex])
break;
//无需真正交换,单向赋值即可
array[parentIndex] = array[childIndex];
parentIndex = childIndex;
childIndex = 2 * childIndex + 1;
}
array[parentIndex] = temp;
}
/**
* 堆排序
* @param array 待排序数组
*/
public static void heapSort(int[] array) {
//构建大根堆
for(int i = (array.length-2)/2;i>=0;i--) {
downAdjust(array, i, array.length);
}
System.out.println(Arrays.toString(array));
//循环删除堆顶元素,移到集合尾部,调整产生新的堆顶
for(int i = array.length -1;i>0;i--) {
//最后1个元素和第1个元素进行交换
int temp = array[i];
array[i] = array[0];
array[0] = temp;
//下沉调整大根堆
downAdjust(array, 0, i);
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] arr = new int[] {1,3,2,6,5,7,8,9,10,0};
heapSort(arr);
System.out.println(Arrays.toString(arr));
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。