时间复杂度O(N*logN),额外空间复杂度O(1)
堆结构非常重要
1,堆结构的heapInsert与heapify
2,堆结构的增大和减少
3,如果只是建立堆的过程,时间复杂度为O(N)
4,优先级队列结构,就是堆结构
完全二叉树:叶子节点从左往右依次补齐
左节点:2*i +1
右节点:2*i+2
父节点:(i-1)/2
一个数组可以对应一个完全二叉树:
大根堆:这里的堆指的是完全二叉树,任何一个子树的最大值都是头部 比如下图:
heapInsert
一个数组可以对应一个完全二叉树,但是如何将一个数组变成大根堆
具体代码如下:
public static void heapSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
for (int i = 0; i < arr.length; i++) {
//依次将添加i位置,形成大根堆
heapInsert(arr, i);
}
。。。。。
public static void heapInsert(int[] arr, int index) {
while (arr[index] > arr[(index - 1) / 2]) {
swap(arr, index, (index - 1) / 2);
index = (index - 1) / 2;
}
}
建立大根堆的时间复杂度:
log1+log2+log3......log(n-1)=O(n)
heapify
如果大根堆中形成的二叉树中某个值改变了,叫做heapify
如图:
假如6变成1
1)找到原始值6的左右孩子(根据公式),将左右俩个孩子进行比较,找到最大的那个
2)比较最大孩子是不是比1大,1和5交换,如下图:
3)1的左右孩子找到最大值5,最后交换结果:
具体代码如下:
public static void heapify(int[] arr, int index, int size) {
int left = index * 2 + 1;
while (left < size) {
//left+1表示右孩子,left + 1 < size 判断右孩子是否越界
//arr[left + 1] > arr[left]判断右孩子是否比左孩子大
//int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left这句代码意思是找到左右孩子最大的那个
int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left;
//左右俩个孩子和当前的数值哪个大 的角标
largest = arr[largest] > arr[index] ? largest : index;
if (largest == index) {
break;
}
//largest!=index,largest和index交换
swap(arr, largest, index);
index = largest;
left = index * 2 + 1;
}
}
heapsize解释:
如何将堆顶弹出:(减堆操作)
1)先让最后一个数和堆顶交换
2)将堆的大小减去1,原先是6,5,43,1对应角标0,1,2,3,4,现在是0,1,2,3,结构如下:
应用利用大根堆和小根堆求中位数:
大根堆每次弹出的值都是大根堆最大值,小根堆每次弹出都是最小值
堆排序就是利用堆结构完成的一个排序:
1)将数组变成大根堆
2)将最后一个位置和堆顶作交换,换完之后,最大值来到数组最后的位置,将堆的大小减去1,将剩下的数做heapify调整;
3)heapify后重新形成大根堆。。。循环
public static void heapSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
for (int i = 0; i < arr.length; i++) {
//依次将添加i位置,形成大根堆
heapInsert(arr, i);
}
int size = arr.length;
swap(arr, 0, --size);
while (size > 0) {
heapify(arr, 0, size);
swap(arr, 0, --size);
}
}