1.堆结构
1.1这里需要用到的数据结构是二叉堆,二叉堆的性质如下:
- 是一个完全二叉树
- 父结点大于(或者小于两个子结点)
- 两个子结点是一个二叉堆
1.2二叉堆的表示方法
使用数组来表示二叉堆,i的子结点是2*i+1 2*i+2 i的父结点是(i-1)/2
1.3二叉堆的插入和删除
为了满足上面二叉堆的性质,二叉堆在插入和删除的时候需要调整整个堆的数据
具体的办法:
增加:把元素插入末尾,然后不断和父结点比较,比较之后判断有没有必要交换,直到某个父结点大于两个子结点
删除:删除根结点,然后把最后一个添加到根结点,然后进行下沉操作,把父结点和两个子结点比较,不满足性质就交换,直到,父元素和两个子元素都满足大小关系
总的来说二叉堆的操作可以看做上浮(增加),下沉(删除)
2.代码实践
总的来说,堆排序包括插入和删除两个部分,插入的时候先插入到最末,然后上浮,删除的时候,删除根元素,然后把最末的提到第一个,然后执行下沉操作。
执行上浮的时候,不断找父结点,然后调整两个子结点和父结点之间的关系,直到某个父结点满足关系。
执行下沉的时候,也是比较父结点和子结点,但是这里,父结点下一步会被赋值成小的那个子结点(最小堆),然后继续递归,到父子关系正常为止
代码里面需要注意的地方:
- 使用currentIndex来指向最后一个元素的下一位,因此需要小心边界元素
- 注意跳出的条件,要么满足父子关系,要么到达边界,到达上边界应该是2*parent<currentIndex-2
/**
* Created by kisstheraik on 16/8/14.
* Description 堆排序的算法实践
* 堆:
* 一棵二叉树,父结点大于所有的子结点,父结点的左右子结点都是二叉堆,
* 一般用数组表示堆,i的父结点是(i-1)/2,i的左右子结点分别是2*i+1 2*i+2
* 每次插入和删除都需要调整
*/
public class Heap {
public int[] list;//用来表示堆的数组
public int currentIndex=0;
public static void main(String[] args){
int dataSize=5;
Heap heap=new Heap(dataSize);
for (int i=0;i<dataSize;i++)
heap.add((int)(Math.random()*100));
int index=0;
while(index<heap.currentIndex){
System.out.print(heap.list[index] + " ");
index++;
}
System.out.println();
index=0;
while(index<dataSize){
System.out.print(heap.delete() + " ");
index++;
}
}
public Heap(int size){
this.list=new int[size];
for(int i=0;i<size;i++){
list[i]=Integer.MAX_VALUE;
}
}
/**
*
* @param i int 新加入的结点
*/
public void add(int i){
//插入一个新的结点,然后下标递增
list[currentIndex++]=i;
//上浮
//获取当前指针
int cp=currentIndex-1;
int parent=(cp-1)/2;
while(parent>=0&&2*parent<currentIndex-2){
int leftChild=2*parent+1;
int rightChild=2*parent+2;
if(list[parent]<=list[leftChild]&&list[parent]<=list[rightChild])break;
if(list[parent]>list[leftChild]){
int tmp=list[parent];
list[parent]=list[leftChild];
list[leftChild]=tmp;
}
if(list[parent]>list[rightChild]){
int tmp=list[parent];
list[parent]=list[rightChild];
list[rightChild]=tmp;
}
parent=(parent-1)/2;
}
}
/**
* @description 删除根部的结点
*/
public int delete(){
if(currentIndex==0)return Integer.MAX_VALUE;
int result=list[0];
//第一个赋值成最后一个
list[0]=list[currentIndex-1];
int parent=0;
while(2*parent<currentIndex-2){
int leftChild=2*parent+1;
int rightChild=2*parent+2;
if(list[leftChild]>=list[parent]&&list[rightChild]>=list[parent])break;
int child=0;
if(list[leftChild]<list[rightChild]){
child=leftChild;
}else child=rightChild;
int tmp=list[parent];
list[parent]=list[child];
list[child]=tmp;
parent=child;
}
currentIndex--;
return result;
}
}
4.性能分析
最好,最坏和平均都是nlogn,其中建立堆的复杂度为O(n)排序为O(nlogn)