package basic_class_01;
/**
* 堆排序
*
* 最差时间分析:O(n*log2n)
* 平均时间复杂度 :O(n*log2n)
* 稳定度:不稳定
* 空间复杂度 :O(1)
*
*
* 堆排序包括两个阶段,初始化建堆和重建堆。
* 初始化建堆的时间复杂度为O(n),
* 排序重建堆的时间复杂度为nlog(n)
* 所以总的时间复杂度为O(n+nlogn)=O(nlogn)
* 另外堆排序的比较次数和序列的初始状态有关,但只是在序列初始状态为堆的情况下比较次数显著减少
* 在序列有序或逆序的情况下比较次数不会发生明显变化。
*
* @author lenovo
*
*/
public class Code_03_HeapSort {
public static void heapSort(int[] arr) {
if(arr == null || arr.length < 2) {
return;
}
// 构建大根堆
for(int i = 0; i < arr.length; i++) {
heapInsert(arr,i);
}
int size = arr.length;
swap(arr, 0, --size); // 首先需要打乱这个大根堆的结构
while(size > 0) {
heapify(arr, 0, size); // 最大的元素放在了大根堆最后的位置了
// heapify又重新构建了一个大根堆
swap(arr,0, --size); // 再一次把这个大根堆的结构打乱 每一次打乱的之后 都会进行排序
}
}
// 将数组生成大根堆
// 这个方法是生成一个大根堆:您需要先了解大根堆的构建与数组之间的转换关系
public static void heapInsert(int[] arr, int index) {
while(arr[index] > arr[(index - 1) / 2]) { // 如果当前index上的元素比父节点的元素要大,那么就跟父节点交换位置
swap(arr, index, (index - 1) / 2); //
index = (index - 1) / 2; // 当前新插入的元素放到了原先的父节点,继续往上判断
}
}
// 大根堆的排序
// 需要注意的是每一次在排序的时候 交换了位置 还是得保证整个堆的结构是大根堆
// 要不然下一次的比较排序就是错的
public static void heapify(int[] arr, int index, int size) {
int left = index * 2 + 1; // 左节点
while(left < size) { // 如果是叶子节点 那么就不要
// 找到左节点 跟 右节点 较大的那一个
int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left;
// 左 右 节点的数字 不一定会比 当前节点要大 所以还得继续判断
largest = arr[largest] > arr[index] ? largest : index;
if(largest == index) {
break; // 如果当前节点 在这个小堆里面的左右连个点是最大的,那么就不用继续进行判断;额
}
swap(arr, largest,index); // 交换之后往上的都是 符合大根堆的结构的 只需要往下去比较
index = largest; // 当前节点需要往下走
left = index * 2 + 1;
}
}
public static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
堆排序 -- 包含笔者 呕心沥血的分析过程
猜你喜欢
转载自blog.csdn.net/qq_38200548/article/details/81262948
今日推荐
周排行