首先我们引入一张思维导图,并且以后的排序博客我将以这位博主的博文为基础,进行代码自我实现,简单介绍以及自我理解。
链接:cnblogs.com/cndarren/p/11787368.html
堆排序的思想:
构建一个大根堆(基于完全二叉树)。当前根就是当前序列的最大值(根节点),与尾结点(未排序的末尾)交换,那么最后一个节点就是最大值了(已排序序列),但是我们破坏了大根堆,然后我们对未排序序列在进行大根堆调整,再把最大值也就是根,与未排序序列的尾结点进行交换。直到未排序序列为0。
有关于图解,请参考链接中的动图,我认为链接中的动图比我画的静态图好太多。
在这里我拿来参考一下。
实现:
//构建大根堆
public static int[] adjust(int[] arr, int start,int end){
int root = arr[start]; //保存当前根节点,也就是起始节点
for(int i = 2*start+1 ; i <= end ; i = i*2 +1){ //i 为当前最大值的下标
//先对比当前二叉树的左右孩子
//首先确保有右孩子
if(i+1 <= end && arr[i+1] > arr[i]){ //有右孩子的同时右孩子比左孩子大
i++; // 那么我们就让右孩子下标变为最大值的下标
}
//再从对比孩子的最大值与根点的大小,如果比父节点大,那么就把左右孩子的最大值放置在父节点上,
// 让需要调整的二叉树的根的下标设置为左右孩子最大值的节点下标,再进行调整。
if(arr[i] > root){
arr[start] = arr[i];
start = i;
}else { //如果左右孩子都不大于根,那么就证明当前的左右孩子的父节点就是根所存放的位置了 跳出循环
break;
}
}
//当我们退出循环的时候,也就证明找到了root节点所需要存放的位置,并且当前根为最大值
arr[start] = root;
return arr;
}
public static int[] heapSort(int[] arr){
//首先让待排序列变为完全二叉树
//从尾节点(最后一个元素)的父节点开始构建二叉树
//子节点——》父节点 (子节点下标 - 1)/ 2 = 父节点下标
//父节点——》子节点 父节点下标 * 2 +1 = 左孩子下标 右孩子下标 = 左孩子下标 +1
for(int i = (( (arr.length-1) -1) /2) ; i >= 0; i-- ){ // arr.length -1 = 尾结点 (尾结点-1 ) /2 = 父节点
adjust(arr,i,arr.length-1); //让当前节点到尾结点直接进行 大根堆 二叉树调整
}
//全部调整完,当前的root节点(根节点就是当前序列的最大值)
for(int i = 0 ; i < arr.length ;i++){ //因为当前是大根堆,所以让最大的值与尾结点置换,那么最后一个节点就是当前序列的最大的值,就是有序的了
int max = arr[0];
arr[0] = arr[arr.length-1-i];
arr[arr.length-1-i] = max;
adjust(arr,0,arr.length-1 -1 -i); //交换之后,我们破坏了大根堆,
// 我们需要让除了当前尾结点之外的元素进行重新构建大根堆
//构建之后成为大根堆,再进入循环,让最大值与当前的序列的尾结点的倒数前n个交换
// (第一次进入循环 n = 0,第二次进入循环 n = 1 ,第一次进入循环之后,倒数第一个数字是最大值,那么就是有序的)
//第二次进入序列,倒数第一个数字已经是当前序列的最大值了,所以与倒数第二个数字进行交换,那么最后两个数字就是有序的
//依次类推
}
return arr;
}
时间复杂度:第一建堆的时间复杂度 O(n * log2 n)
空间复杂度:O(1)
稳定性:不稳定 父与子属于跳跃式交换,不稳定