堆结构及堆排序
前置知识
- 堆 是完全二叉树。
- 完全二叉树:如果一个树是满的,就是完全二叉树,如果一个树不满,但它处在变满的路上,且是从左往又依次变满,也叫完全二叉树,空数也算完全二叉树
- 数组对应成完全二叉树 :数组中 下标 i 位置 在 完全二叉树中 ,它的左孩子 : 2i +1 , 右孩子 : 2i+2, 父节点:(i-1)/2 向下取整
- 大根堆 :每棵子数的最大值都是头节点的值
- 小根堆:每棵子数的最小值都是头节点的值
堆排序(复杂度 O(NlogN))
- 把数组变成大根堆结构,建立堆的过程:
- 从上到下的方法heapInsert,时间复杂度为0(NlogN)
2)从下到上的方法heapify,时间复杂度为O(N)
- 把堆的最大值和堆末尾的值交换,然后减少堆的大小之后,再去调整堆,一直周而复始,时间复杂度为O(NlogN)
- 堆的大小减小成0 之后,排序完成
//从下往上,和父节点比 ,比父节点大,则和父节点交换,index来到父节点位置
//不比父节点大 停
private static void heapInsert(int[] arr,int index){
while (arr[index] > arr[(index-1)/2]){ //负数 也是向上取整, index = 0 时, (index-1)/2 = 0,arr[0] > arr[0] 不满足,跳出循环
swap(arr, index, (index-1)/2);
index = (index-1)/2;
}
}
//从上往下,和较大的子节点比,比较大的子节点小,则和较大的子节点交换 ,
//没有子节点 或不比较大的节点小 停
private static void heapify(int[] arr,int index, int heapSize){
int left = 2*index +1; //左子节点的下标
while (left < heapSize){ //完全二叉树 左节点不存在 则不可能有右节点,跳出循环,左节点存在,右节点可能存在可能不存在
int maxIndex = 2*index + 2 < heapSize && arr[2*index +2] > arr[2*index +1] ? 2*index +2 : 2*index +1; //右节点存在切左节点的数比左节点的大,则返回右节点的下标作为子节点最大值下标,否则返回左节点
if (arr[index] < arr[maxIndex]){
swap(arr,index,maxIndex);
index = maxIndex;
}else{
break; //不比较大的节点小 跳出循环
}
}
}
public static void main(String[] args) {
int[] arr = {2,4,7,5,4,3,1,8,2,54,32};;
//调整成大根堆 方法1:heapInsert 复杂度O(NlogN)
for (int i = 0; i < arr.length; i++) {
heapInsert(arr, 0);
}
//调整成大根堆 方法2:heapify 复杂度O(N) 前提:必须给定了完整数组
int heapSize = arr.length; 记录堆的长度,最开始等于数组的长度
for (int i = arr.length -1; i >= 0; i--) {
heapify(arr, i,heapSize);
}
//调整后的堆的最大值在0 位置,0 位置 和堆的最末尾位置交换,堆大小减一, 堆从0位置开始heapify调整成大根堆,循环往复,知道 堆的大小变为0
while (heapSize > 0){
swap(arr, 0, --heapSize);
heapify(arr, 0,heapSize);
}
}
private static void swap(int[] arr,int i, int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}