1.1 堆的定义
**堆是计算机科学中一类特殊的数据结构的统称,堆通常可以被看做是一
棵完全二叉树的数组对象。**
堆的特性
1.它是完全二叉树,除了树的最后一层结点不需要是满的,其它的每一
层从左到右都是满的,如果最后一层结点不是满的,那么要求左满右不
满。
2.它通常用数组来实现。根结点在位置1,如果一个结点的位置为k则
它的父结点的位置为[k/2],而它的两个子结点的位置则分别为2k和
2k+1。
这样,在不使用指针的情况下,我们也可以通过计算数组的索引在树中
上下移动:从a[k]向上一层,就令k等于k/2,向下一层就令k等于2k或
2k+1。
3.每个结点都大于等于它的两个子结点。这里要注意堆中仅仅规定了
每个结点大于等于它的两个子结点,但这两个子结点的顺序并没有做
规定,跟我们之前学习的二叉查找树是有区别的。
堆的实现
public class Heap<T extends Comparable<T>> {
private T[] data;
//堆中元素的个数
private Integer N;
public Heap(int capacity) {
this.data = (T[]) new Comparable[capacity+1];
this.N = 0;
}
//根据堆中元素的索引获得其左孩子节点的索引
private int leftChild(int index) {
return index * 2 + 1;
}
//根据堆中元素的索引获得其右孩子节点的索引
private int rightChild(int index) {
return index * 2 + 2;
}
//交换两个元素的值
private void exch(int i,int j){
T temp = data[i];
data[i] = data[j];
data[j] = temp;
}
private boolean less(int i,int j){
return data[i].compareTo(data[j])<0;
}
//上浮算法
private void siftUp(int k){
//通过循环,不断的比较当前结点的值和其父结点的值,如果发现父结点的值比当前结点的值小,则交换位置
while(k>1){
//比较当前结点和其父结点
if (less(k/2,k)){
exch(k/2,k);
}
k = k/2;
}
}
public void add(T e) {
//总是直接插入到堆的末尾处
data[++N]=e;
//上浮算法进行排序
siftUp(N);
}
public T delMax(){
T max = data[1];
//总是删除的是堆顶的数据
if(N<= 0){
return null;
}
//交换堆顶和最后一个的数据
exch(1,N);
data[N] = null;
//下沉对堆顶
N--;
siftDown(1);
//元素个数减1
return max;
}
//下沉算法
public void siftDown(Integer index){
while (index*2<=N){
int maxIndex;
if (index*2+1>N){
maxIndex = index*2;
}else{
//找到左右子节点的值最大的一个
maxIndex = data[index*2].compareTo(data[index*2+1])<0?index*2+1:index*2;
}
if (data[index].compareTo(data[maxIndex])>0){
break;
}
//交换最大索引处的值和当前索引
exch(index,maxIndex);
//
index = maxIndex;
}
}
public static void main(String[] args) {
Heap<String> heap = new Heap<String>(9);
heap.add("A");
heap.add("B");
heap.add("C");
heap.add("D");
heap.add("E");
heap.add("F");
heap.add("G");
//通过循环从堆中删除数据
String result = null;
while((result = heap.delMax())!=null){
System.out.print(result+" ");
}
}
思维导图
这个导图,是根据自己的理解画的,所以里面的大白话比较多。
堆排序
实现步骤:
1.构造堆;
2.得到堆顶元素,这个值就是最大值;
3.交换堆顶元素和数组中的最后一个元素,此时所有元素中的最大元素已经放到合适的位置;
4.对堆进行调整,重新让除了最后一个元素的剩余元素中的最大值放到堆顶;
5.重复2~4这个步骤,直到堆中剩一个元素为止。
堆的构造,最直观的想法就是另外再创建一个和新数组数组,然后从左往右遍历原数组,每得到一个元素后,添加
到新数组中,并通过上浮,对堆进行调整,最后新的数组就是一个堆。
上述的方式虽然很直观,也很简单,但是我们可以用更聪明一点的办法完成它。创建一个新数组,把原数组
0length-1的数据拷贝到新数组的1length处,再从新数组长度的一半处开始往1索引处扫描(从右往左),然后
对扫描到的每一个元素做下沉调整即可。