您的位置:首页 > 理论基础 > 数据结构算法

数据结构之排序算法(一)-堆排序

2015-12-21 14:03 537 查看
继上篇文章讲到堆的实现之后http://blog.csdn.net/tuke_tuke/article/details/50357939,下面可以使用堆来实现堆排序。

在堆的基础上实现堆排序的思路很简单:(这里使用最小堆,当然最大堆也可以)

   最小堆MinHeap就是最小的数在堆的根元素的位置。首先把一组数建堆,然后再不断的移除堆的根元素,由于每次移除的根元素都是现有堆的最小元素,故可得到所有元素的从小到大的顺序。

MinHeap.java

package heapsort;

import java.util.ArrayList;

public class MinHeap <E extends Comparable>{
private ArrayList<E> list=new ArrayList<E>();//用数组实现堆

public MinHeap(){}
public MinHeap(E[] objects){
for(int i=0;i<objects.length;i++){
add(objects[i]);
}
}
public void add(E newObject){//添加一个元素
list.add(newObject);
int currentIndex=list.size()-1;

while(currentIndex>0){
int parentIndex=(currentIndex-1)/2;//找到该结点的父结点
if(list.get(currentIndex).compareTo(list.get(parentIndex))<0){
//如果当前结点的值小于父结点就交换位置
E temp=list.get(currentIndex);
list.set(currentIndex, list.get(parentIndex));
list.set(parentIndex, temp);
}
else
break;
currentIndex=parentIndex;
}
}

public E remove(){//删除并返回根结点
if(list.size()==0) return null;

E removeObject=list.get(0);
list.set(0, list.get(list.size()-1));//把最后一个结点放在根结点的位置
list.remove(list.size()-1);

int currentIndex=0;
while(currentIndex<list.size()){
int leftChildIndex=2*currentIndex+1;
int rightChildIndex=2*currentIndex+2;//左右孩子结点的坐标

if(leftChildIndex>=list.size())break;
//比较左右孩子的值,使maxIndex指向值小的结点
int minIndex=leftChildIndex;
if(rightChildIndex<list.size()){
if(list.get(minIndex).compareTo(list.get(rightChildIndex))>0){
minIndex=rightChildIndex;
}
}
//如果当前结点的值大于其左右孩子中的大的值,就交换两个结点
if(list.get(currentIndex).compareTo(list.get(minIndex))>0){
E temp=list.get(minIndex);
list.set(minIndex, list.get(currentIndex));
list.set(currentIndex, temp);
currentIndex=minIndex;
}
else
break;
}

return removeObject;

}

public int getSize(){
return list.size();
}
}
HeapSort.java
package heapsort;

import java.awt.List;

public class HeapSort {

public static<E extends Comparable> void heapSort(E[] list){
MinHeap<E> heap=new MinHeap<E>();//最小堆类
//先把数组添加到堆中,建堆
for(int i=0;i<list.length;i++){
heap.add(list[i]);
}
//然后在一次删除根结点,根结点总是最值
/*for(int i=list.length-1;i>=0;i--){//利用最大堆排序
list[i]=heap.remove();
}*/
for(int i=0;i<=list.length-1;i++){//利用最小堆排序,不断的移除堆的根元素
list[i]=heap.remove(); //每次都会调整堆
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Integer[] list={22,43,11,24,27,21,54,35,23};
System.out.println("堆排序前的数组是:");
for(int i=0;i<list.length;i++){
System.out.print(list[i]+" ");
}
heapSort(list);//堆排序
System.out.println();
System.out.println("堆排序后的数组是:");
for(int i=0;i<list.length;i++){
System.out.print(list[i]+" ");
}
}

}



算法分析

   运行时间主要是消耗在初始构建堆在重建堆时的反复筛选上。

在构建堆的过程中,因为我们是完全二叉树从最下层最右边的非终端结点开始构建,将它与其孩子进行比较和若有必要的互换,对于每个非终端结点来说,其实最多进行两次比较和互换操作,因此整个构建堆的时间复杂度为O(n)。

在正式排序时,第i次取堆顶记录重建堆需要用O(logi)的时间(完全二叉树的某个结点到根结点的距离为log2i+1),并且需要取n-1次堆顶记录,因此,重建堆的时间复杂度为O(nlogn)。

总体来说,堆排序的时间复杂度为O(nlogn)。由于堆排序对原始记录的排序状态并不敏感,因此它无论是最好、最坏和平均时间复杂度均为O(nlogn)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  堆排序 数据结构