堆排序
一个堆需要满足两个条件:
- 是一颗完全二叉树(节点从上到下,从左到右)
- 每个节点大于它自生的两个子节点,成为大根堆(或者每个节点小于它两个子节点,称为小根堆)
堆排序的核心思想:
1. 创建堆:
-
从最后一个父节点 i 开始直到起始节点依次做heapify
-
创建堆的重要方法——heapify(下面有图片样例): 若对节点 i 进行heapify操作,就是从节点 i 和其两个子节点中选择一个最大的与节点i交换,这里有两种情况:
1.若最大的在子节点中,交换后再对交换位子的子节点做heapify,直到最后一个节点为止。
2.若最大节点为本身,则结束 -
堆中的父节点和子节点求法:
parent =(i - 1)/ 2;
c1=2i + 1;
c2=2i + 2;
2. 对堆进行排序:
- 从最后一个元素i开始,依次向前递减(i- -),对于每次的i都与当前的首节点交换位置,交换位置后立刻对首节点进行n-i的heapify操作,(因为此时堆被破坏),又因为只有首元素被破坏,所以只需要heapify(tree,n-i,0)即可重建堆.当i为0时,即排序完成。
时间复杂度:
- O(nlog2^n)
稳定性
- 不稳定(在建堆的时候或者进行heapify时都可能交换原本相同的两个元素)
java源码
public class HeapSort {
//heapify函数
public static void heapify(int []tree,int n,int i){
if(i==n){//到最后节点跳出
return;
}
int c1=2*i+1;
int c2=2*i+2;
int maxIndex=i;
if(c1<n&&tree[c1]>tree[maxIndex]){//获得最小节点位置
maxIndex=c1;
}
if(c2<n&&tree[c2]>tree[maxIndex]){//获得最小节点位置
maxIndex=c2;
}
if(maxIndex!=i){
int temp=tree[maxIndex];tree[maxIndex]=tree[i];tree[i]=temp;
heapify(tree,n,maxIndex);//再将交换节点位置处的元素进行heapify操作
}
}
//创建堆
public static void build_heap(int []tree){
int last_node=tree.length-1;
int last_parent=(last_node-1)/2;//从最后一个父节点开始,向前依次做heapify操作
for(int i=last_parent;i>=0;i--){
heapify(tree,tree.length,i);
}
}
//堆排序
public static void heap_sort(int[]tree){
build_heap(tree);//先建堆
for(int i=tree.length-1;i>=0;i--){//i从最后一个元素开始,依次向前(i--),分别与首节点交换位置,交换位置后立刻对首节点进行n-i的heapify操作,这样能保证每次交换的首元素都是最大的
int temp=tree[i];
tree[i]=tree[0];
tree[0]=temp;
heapify(tree,i,0);
}
}
public static void main(String[] args) {
//测试
int []a={2,5,3,1,11,5,5,595,9,5,4,4,8,4,54,1,1,12,1,10,4};
heap_sort(a);
for(int t:a)System.out.println(t);
}
}