标签:
继上篇文章讲到堆的实现之后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)
标签:
原文地址:http://blog.csdn.net/tuke_tuke/article/details/50371490