首先堆是一种以顺序结构存储的完全二叉树,本质上还是数组。
为什么是完全二叉树?由于堆是以顺序结构存储的,如果是非完全二叉树,必然会造成空间的浪费。
堆分为大根堆和小根堆:
大根堆是指每个根节点的值都大于其左右子树的值;
小根堆是指每个根节点的值都小于其左右子树的值。
已知双亲结点的下标为i,则其左右孩子的下标分别为 2*i+1 , 2*i+2 。
已知左右孩子的下标,则其双亲结点的下标统一为 ((左孩子下标/右孩子下标-1)/ 2) 。
并且优先级队列默认就是小根堆
1.首先将待排序元素建立成大根堆(对应的是升序), 或 小根堆(对应的是降序)。
2.交换堆顶元素和堆尾元素,此时堆尾的元素默认已经排好序,以后就不用动了。
3.排除交换后的堆尾元素,即在剩下的元素中进行建立大根堆。
4.再次再剩下的元素中交换堆顶元素和堆尾元素。此时堆尾的元素默认已经排好序,以后就不用动了。
5.重复3,4操作。直到排好序。
public static void HeapSort(int[] elem){ //堆排序
//首先将待排序元素建立成大根堆 或 小根堆
//以大根堆为例
createBigHeap(elem,elem.length); //创建大根堆
int size = elem.length-1;
while(size>0) {
//交换堆顶元素和堆尾元素 交换完后代表此时的堆尾已经排好序 后面就不用在参与到创建大根堆中了
int tmp = elem[0];
elem[0] = elem[size];
elem[size] = tmp;
//再在剩下的元素中创建大根堆
createBigHeap(elem,size);
size--;
}
}
public static void createBigHeap(int[] elem,int end) { //向下调整 时间复杂度O(n)
int size = end;
for (int parent = (size-1-1)/2; parent >= 0; parent--) {
int child = 2*parent+1;
while(child < size) {
if (child < size - 1 && elem[child] < elem[child + 1]) {
child++;
}
//已经得到左右孩子中最大的值了
if (elem[parent] < elem[child]) {
int tmp = elem[parent];
elem[parent] = elem[child];
elem[child] = tmp;
parent = child;
child = parent * 2 + 1;
} else {
break;
}
}
}
}
public static void main(String[] args) {
int[] elem = {56,89,21,32,20,10,2,5,8,645,87,2,6,3,56,94,122,30};
HeapSort(elem);
for (int x:elem) {
System.out.print(x+" ");
}
}
要实现降序排序的话需要将创建大根堆改为创建小根堆。
创建大小根堆过程分为向下调整和向上调整,向下的时间复杂度O(n)。要优于向上。
1.创建大根堆代码 以向下调整为例
public static void createBigHeap(int[] elem,int end) { //大根堆 向下调整 时间复杂度O(n)
int size = end;
for (int parent = (size-1-1)/2; parent >= 0; parent--) {
shiftDown(parent,size,elem); //向下调整
}
}
private static void shiftDown(int parent,int size,int[] elem) { //向下调整
int child = 2*parent+1;
while(child < size) {
if (child < size - 1 && elem[child] < elem[child + 1]) {
child++;
}
//已经得到左右孩子中最大的值了
if (elem[parent] < elem[child]) {
int tmp = elem[parent];
elem[parent] = elem[child];
elem[child] = tmp;
parent = child;
child = parent * 2 + 1;
} else {
break;
}
}
2.向下调整建堆的时间复杂度推导
创建小根堆代码
public static void createSmallHeap(int[] elem,int end) {//创建小根堆 向下调整 时间复杂O(n)
int size = end;
for (int parent = (size-1-1)/2; parent >= 0; parent--) {
shiftDown(parent,size,elem); //向下调整
}
}
private static void shiftDown(int parent,int size,int[] elem) {
int child = 2*parent+1;
while(child < size) {
if (child < size - 1 && elem[child] > elem[child + 1]) {
child++;
}
//已经得到左右孩子中最小的值了
if (elem[parent] > elem[child]) {
int tmp = elem[parent];
elem[parent] = elem[child];
elem[child] = tmp;
parent = child;
child = parent * 2 + 1;
} else {
break;
}
}
}