大顶堆和小顶堆
结合顺序存储二叉树的概念
大顶堆映射到数组就是arr[i] >= arr[2i+1] && arr[2i+2]
小顶堆就是arr[i] <= arr[2i+1] && arr[2i+2]
一般升序就是用的大顶堆
降序就用小顶堆
import java.util.Arrays;
public class DuipaixuTest {
public static void main(String[] args) {
int[] arr = {
4,8,9,2,5,7,5,-54612,0,513,54,11,9863,20,0,543,522,320,96,7};
duipaixuShengxu(arr);
}
//先写一个算法
/**
* 功能就是, 能够任意输入一个子树的父节点, 这个算法就能够将该节点作为父节点的子树变成一个大顶堆
* @param arr 顺序存储二叉树的数组
* @param i 子树的父节点
* @param length 数组的长度
*/
static public void getBigdingduishu(int[] arr, int i, int length){
int temp = arr[i];
for (int k = 2*i+1; k<length; k = 2*k+1){
//能进这个循环说明这个节点(i)有左节点, 这个k = 2*k+1就是这个循环的关键, 意味着能进这个循环k就指向当前节点的左节点
if (k+1 < length && arr[k] < arr[k+1]){
//能进这个if说明这个节点(i)有右节点, 并且右节点比左节点大
k++;
}
if (arr[k] > temp){
//这个if就是整个算法的关键, 能进这个if说明子节点大于父节点
arr[i] = arr[k];
i = k; //并且将当前这个子节点变成当前节点(i), 这个我们就发现, 当前节点(i)已经改变, 但是 我们这个if判断的依然是temp, 也就是最初进来算法的那个i
//这个并不马上将temp给arr[i], 但是我们实际上假设此时的arr[k],也就是arr[i]已经等于了temp
}else{
//如果没有大于父节点, 那么就直接退出循环
break;
}
}
//这个循环就保证了每一个子树都是大顶堆, 但是如果想要将整个树都变成大顶堆, 那么就要从最后一个子树开始, 将一个一个子树都变成大顶堆
//退出循环之后, 此时的i, 也就是temp应该放的位置
arr[i] = temp;
}
//有了以上的算法以后, 就可以进行堆排序的工作了
//这里是使用大顶堆将arr进行升序排序
static public void duipaixuShengxu(int[] arr){
int temp = 0;
System.out.println("堆排序之后的数列是");
//第一步先将这个数组变成一个大顶堆
//其实就是从最后一个非叶子节点开始, 也就是最后一个子树开始, 将所有子树变成一个大顶堆
for (int i = arr.length/2-1; i>=0; i--){
//很显然0就是整个树的根节点, 也是最后一个变成大顶堆的子树
getBigdingduishu(arr, i, arr.length);
}
//第二步就是, 整个树变成大顶堆之后, 就把arr的第一个和最后一个进行交换
//交换完了之后就arr.length减一, 也就是将最大的值沉到了最低部, 其实这个时候除了最后的这个整个树不是大顶堆, 其他的所有子树都是大顶堆的状态
//所以只需要将整个数在进行一次以上的算法变成大顶堆就可以继续交换了
//一共应该交换机次呢
//就是arr.length-1次
for (int j = arr.length-1; j>=1; j--){
temp = arr[j];
arr[j] = arr[0];
arr[0] = temp;
getBigdingduishu(arr, 0, j);
}
System.out.println(Arrays.toString(arr));
}
}
堆排序的速度是非常快的, 时间复杂度是O(nlogn), 线性对数阶