堆的主要性质
1、堆有两种:大顶堆和小顶堆,大顶堆是指每个结点的值都大于其左右孩子的值(小顶堆反之)。
2、堆和数组下标的关系
根结点是从 0 开始存储,所以假设某个元素为序号为 i ,那么其左子树的位置是 2i+1 ,其右子树的位置是 2i+2 ,其父节点的位置是 (n-1)/2 。
3、堆和树的关系
堆是完全二叉树,完全二叉树:(1)除最后一层外,每一层上的节点数均达到最大值;(2)最后一层只缺少若干右边结点。
4、堆和栈的关系
栈内存由操作系统来分配和释放,堆内存由程序员自己分配和释放。
5、堆排序的时间复杂度为O(NlogN)
堆排序
一、构造初始堆
1、初始化堆是对所有非叶子结点进行筛选,筛选只需要从第[n/2](向下取整)个父结点开始,从后往前进行结点的调整。
2、父结点调整
(1)父结点调整的目的是让父结点以后的分支都满足大顶堆或小顶堆的性质。
(2)具体步骤
如果父、左儿子和右儿子中,父是最大值,则不需要调整;
如果父不是最大值,则先把父与最大儿的值交换,再递归判断最大儿处结点是否需要调整。
二、堆排序
1、将array[0]与最后一个元素交换,这样最后一个结点就是最大值,然后在 0~n-2 范围内进行根结点调整;
2、将array[0]与倒数第二个元素交换,再在 0~n-3 范围内根结点调整;
3、重复以上步骤 array.length-1 次。
DEMO
public class MyHeapSort {
static int[] array;
public static void main(String[] args) {
input();
heapSort(array);
output();
}
static void input(){
System.out.println("请输入一个数组,数字之间以英文逗号分隔:");
Scanner scanner = new Scanner(System.in);
String string = scanner.next().toString(); //读取输入,直到遇到空格或换行,并转化为字符串。
String arrayString[] = string.split(","); // 分割为字符串数组
array = new int[arrayString.length]; // 定义数组长度
for(int i=0;i<array.length;i++){
array[i] = Integer.parseInt(arrayString[i]); // 整型化
}
}
static void output() {
System.out.println("经过堆排序后,结果如下:");
for(int i=0;i<array.length;i++)
System.out.print(array[i] + " ");
}
static void heapSort(int[] array){
int temp;
// 构建初始化堆
buildMaxHeap(array);
// 堆排序
for(int i=array.length-1;i>0;i--){ // n个数需要n-1次的结点交换和调整
temp=array[0]; array[0]=array[i];array[i]=temp; // 交换array[0]与array[i]
fatherNodeAdjust(array, i, 0); // 在 0~i 内进行结点调整
}
}
static void buildMaxHeap(int[] array) { // 构建初始化堆
// 由于完全二叉树的性质,只需要从第[n/2]个父结点开始,从后往前进行父结点调整。
int half = array.length / 2;
for (int i = half; i >= 0; i--) {
fatherNodeAdjust(array, array.length, i);
}
}
static void fatherNodeAdjust(int[] array, int heapSize, int index) { // 父结点调整
int left = index * 2 + 1; // 左孩子
int right = index * 2 + 2; // 右孩子
int largest = index; // 暂时将父结点定为最大值
int temp;
// 先要判断左儿子是否存在,再判断左儿子是不是更大。
if (left<heapSize && array[left]>array[index]) {
largest = left;
}
// 右儿子是否存在,右儿子是否更大。
if (right<heapSize && array[right]>array[largest]) { // 右孩子存在且右孩子更大
largest = right;
}
if (index != largest) {
// 如果发现父不是最大值,则父与最大儿交换,以确保父最大。
temp = array[index]; array[index]=array[largest]; array[largest]=temp;
// 父放在最大儿位置上,是否满足最大堆的性质,还需调用递归maxHeap()来调整。
fatherNodeAdjust(array, heapSize, largest);
}
}
}
常见题目
下列数据结构不是多型数据类型的是()
A 堆 B 栈 C 字符串 D 有向图
下标从1 开始,在含有n个关键字的小根堆中,关键字最大的记录有可能存储在()位置上
A [n/2] B [n/2]-1 C 1 D [n/2]+2
【解析】
小根堆中最大的数一定是放在叶子节点上
堆本身是个完全二叉树
完全二叉树的叶子节点的位置大于 n/2
选D