堆排序
定义
堆排序是利用堆这种数据结构所设计的一种排序算法。
堆是一种近似完全二叉树的结构,并同时满足堆积的性质:子结点的键值或索引总是小于(或者总是大于)它的父结点
子结点的键值或索引值总是小于其父结点:大根堆
子结点的键值或索引值总是大于其父结点:小根堆
稳定性
不稳定
时间复杂度
O(N*logN)
建堆方法
HeapInsert 上浮
描述
假定事先不知道有多少个元素,通过不断往堆里面插入元素进行调整来构建堆
步骤
- 增加堆的长度,在最末尾的地方加入最新插入的元素
- 比较当前元素和它的父结点值,如果比父结点值大,则交换两个元素,否则返回
- 重复2
时间复杂度
O(N * logN)
示例
-
给定⽆序序列,通过 HeapInsert 建⽴⼤根堆。
-
插⼊节点[6],与⽗节点[3]进⾏⽐较,并交换。
-
插⼊节点[8],与⽗节点[6]进⾏⽐较,并交换。
-
插⼊节点[5],与⽗节点[3]进⾏⽐较,并交换。
-
插⼊节点[9],与⽗节点[5]进⾏⽐较,并交换。
-
节点[9]继续与⽗节点[8]进⾏⽐较,并交换。
-
所有节点插⼊,建堆完成。
Heapify 下沉
从最后一个非叶子结点一直到根结点进行堆化的调整。
以大根堆为例,如果当前结点小于某个自己的孩子结点,那么当前结点和这个孩子交换,并持续往下递归调整
时间复杂度
O(N)
第一个非叶子结点
如果根结点在数组中的索引为0,那么最后 一个非叶子结点计算公式lastNonLeaf = (arr.length - 2)/2
设最后一个非叶子结点的位置为x,则最后一个叶子结点一定是2x + 1
或 2x + 2
示例
- 给定⽆序序列,通过 Heapify 建⽴⼤根堆。
- 从最后⼀个⾮叶⼦节点[6]开始调整(arr.length/2-1),与其孩⼦节点中的较⼤节点⽐较,并交换。
- 调整节点[5],与其孩⼦节点中的较⼤节点⽐较,并交换。
- 继续调整节点[5],与其新孩⼦节点中的较⼤节点⽐较,并交换。
- 所有⾮叶⼦节点调整完成,建堆完成。
代码
测试用例
int arr[] = {3,6,8,5,9};
原代码
/**
* @Classname HeapSort
* @Description 堆排序
* 时间复杂度 O(N * logN)
*/
public class HeapSort {
public static void main(String[] args) {
// 测试数据
int arr[] = {3,6,8,5,9};
//测试
maxHeapSort(arr,arr.length);
}
/**
* 堆排序
* @param arr
* @param len
*/
public static void maxHeapSort(int[] arr,int len){
System.out.println("初始数据:"+Arrays.toString(arr));
// 最后一个非叶子结点(len-2)/2 最后一个结点:len-1
for(int i = len/2 - 1; i>=0; i--){
System.out.println("父结点下标:"+i);
maxHeapify(arr,i,len - 1);
}
// 建堆完毕后结果输出
System.out.println("建堆完毕后结果:"+Arrays.toString(arr)+"\n");
// 先将第一个元素和已排好的元素前一位做交换(将最大的元素放到后面,类似于直接插入排序),再重新调整,直到排序完毕
for (int i = len -1;i>0;i--){
System.out.println("本次排序范围:0~"+(i-1));
swap(arr,0,i);
maxHeapify(arr,0,i-1);
}
System.out.println("最终结果:"+Arrays.toString(arr));
}
/**
* 建堆
* @param arr
* @param start
* @param end
*/
public static void maxHeapify(int arr[],int start,int end){
// 父结点和子结点下标
int dad = start;
int son = dad * 2 +1;
while(son <= end){ // 子结点在范围内
if (son + 1 <= end && arr[son] < arr[son +1]){
son++;
}
if (arr[dad]>arr[son]){ // 父结点大于子节点 跳出函数
return;
} else { // 否则交换父结点,并继续子结点和孙结点比较
swap(arr,dad,son);
dad = son;
son = dad * 2 +1;
}
System.out.println("本次交换结果:"+Arrays.toString(arr));
}
}
/**
* 交换函数
* @param arr
* @param i
* @param j
*/
public static void swap(int[] arr,int i ,int j){
int temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
}
}
运行结果
步骤
将堆顶结点与末尾结点交换,使末尾结点最大。然后继续调整堆,再将堆顶结点与末尾结点交换,得到第二大结点。如此反复进行交换 重建
- 已有⼤根堆,对其进⾏堆排序。
- 将堆顶节点[9]与末尾节点[5]进⾏交换,交换完成后将节点[9]移出⼆叉树,并对节点[5]进⾏Heapify调
整。
- 将堆顶节点[8]与末尾节点[3]进⾏交换,交换完成后将节点[8]移出⼆叉树,并对节点[3]进⾏Heapify调
整
- 将堆顶节点[6]与末尾节点[5]进⾏交换,交换完成后将节点[6]移出⼆叉树,并对节点[5]进⾏Heapify调
整。
- 将堆顶节点[5]与末尾节点[3]进⾏交换,交换完成后将节点[5]移出⼆叉树,此时只剩⼀个节点,堆排序
结束。