堆肯定是完全二叉树。
完全二叉树,每个节点的值>=左右节点的称为大顶堆。否则是小顶堆。
“完全二叉树的特点:叶子结点只能出现在最下层和次下层,且最下层的叶子结点集中在树的左部。”
排序就排序和二叉树有什么关系呢?我们实际上可以用数组表示二叉树
832.8父节点,32分别人左右儿子节点 。
只有完全二叉树才有这种规则:
N[i] 的父节点为N[(i - 1)/2]
N[i] 的左节点为N[(2i + 1)]
N[i] 的右节点为N[(2i + 2)]
堆排序也分两步:
1.先建立一个维护堆的函数,一旦发生交换,就要维护,保持数组一直是堆结构。并且能用这个函数从底层开始建堆。
2.维护堆数组后,那么arr[0]就是最大的数,我们那最后一个元素不断和她交换,就能获取最大值,放在数组末(升序排列)。
交换后继续维护,此时传入维护的数组长度-1。又是最大值arr[0]如此往复,最终排序成功。
时间复杂度: 建堆O(n)
维护堆的函数:建堆O(logN)
所以最终O(NlogN)
public class QuickSort {
public static void main(String[] args) {
int[] arr = {-9,78,0,23,-567,70};
build(arr,arr.length);
System.out.println(Arrays.toString(arr));
}
//建立具有堆特征的数组
public static void build(int[] arr,int n){
int i = 0;
//数组最后一个数的父节点(n - 1 - 1) / 2
for (i = (n - 1 - 1) / 2;i >= 0;i--){
quickSort(arr,i,n);
}
//建堆完成,就要开始排序了,由于是大顶堆
//我们把最顶元素与,最后一个元素交换,并且断开最后一个元素的连接,放入数组最后
//就能实现从小到大排序了
for (i = n - 1;i > 0; i--){
int temp = arr[i];
arr[i] = arr[0];
arr[0] = temp;
//交换一次再次维护数组堆,剩余i个元素,n-1。
quickSort(arr,0,i);
}
}
//这个方法,负责每次检查二叉树是否为大小堆。同时能负责刚开始的建立堆
public static void quickSort(int[] arr,int i,int n){
//i表示父节点,n表示数组长度
int parent = i;
int left = 2 * parent + 1;
int right = 2 * parent + 2;
if (left < n && arr[left] > arr[parent]){
parent = left;
}
if (right < n && arr[right] > arr[parent]){
parent = right;
}
//表名发生了树结构的改变,我们需要继续校验,受到影响的树。因此需要递归
if (parent != i){
int temp = arr[parent];
arr[parent] = arr[i];
arr[i] = temp;
//i换到parent的位置了,我们需要维护更换后的节点
quickSort(arr,parent,n);
}
}
}