码迷,mamicode.com
首页 > 其他好文 > 详细

算法笔记之堆排序

时间:2014-08-01 00:11:00      阅读:269      评论:0      收藏:0      [点我收藏+]

标签:style   blog   http   color   java   使用   strong   io   

一、对堆排序的相关了解

1、堆排序的运行时间是 O(nlogn)

2、定义:

heap是一棵具有以下属性的二叉树——

1)它是一棵完全二叉树;

2)每个结点大于或等于它的任意一个孩子。

 

备注:完全二叉树的定义——除了最后一层没填满以及最后一层的叶子都是偏左放置的,其他层都是满的二叉树!

             

3、二叉堆有两种:最大堆和最小堆。在堆排序中我们使用的是最大堆,最小堆常常在构造优先队列时使用。                            

  4、一条路径的深度指的是这条路径的边数,一个结点的深度是指从根结点到该结点的路径的长度。                

                                  

二、对堆进行排序

1)添加新元素

我们可以把堆存储在一个Arraylist里,树根在位置0处,它的两个孩子在位置12。存储规律如下,对于位置在i的结点,它的左孩子在2i+1处,右孩子在2i+2处,而它的父亲在位置(i-1)/2


//增加一个元素
	public void add(E object){
		list.add(object);//先把它放在最后面
		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(parentIndex);
				list.set(parentIndex, list.get(currentIndex));
				list.set(currentIndex, temp);
			}else {
				break;
			}
			currentIndex = parentIndex;//一路交换上去,直到根结点为止
		}
	}
	


2)删除并返回当前堆的最大值

返回根元素,并重建堆,重建堆的思路是将最后面的那个元素放到根元素的位置,然后与它的最大子树想比(之前判断是否有左右子树,然后再比较出左右子树哪个更大),如果比最大子树小,那么交换,依次下去,直到当前元素的索引>=list的长度

public E remove(){
		if (list.size()== 0) {
			return null;
		}else{
			
			E root = list.get(0);
			int last = list.size()-1;
			list.set(0,list.get(last));
			list.remove(last);
			
			int currentIndex = 0;
			while(currentIndex < list.size()){
				//它的左右子树索引
			    int leftChildIndex = 2*currentIndex + 1;
			    int rightChildIndex = 2*currentIndex + 2;
			    
			    //找出最大的子树
			    int maxChildIndex = leftChildIndex;
			    if (leftChildIndex >= list.size()) {
					break;
				}else if(rightChildIndex < list.size()){
					if (list.get(maxChildIndex).compareTo(list.get(rightChildIndex)) < 0) {
						maxChildIndex = rightChildIndex;
					}
				}
			    
			    //与根元素比较,根元素小则交换,否则退出循环,此时已经是堆了
			    if (list.get(currentIndex).compareTo(list.get(maxChildIndex)) < 0) {
					E temp = list.get(currentIndex);
					list.set(currentIndex, list.get(maxChildIndex));
					list.set(maxChildIndex, temp);
					//更新当前索引
					currentIndex = maxChildIndex;
				}else {
					break;
				}
			}
			
			//返回根元素,堆的最大值
			return root;
		}
	}

3、排序


/**
	 * 堆排序方法
	 * decription:
	 * @author : linjq
	 */
	  public  <E extends Comparable> void sort(E[] list) {
	    Heap<E> heap = new Heap<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();
	  }

4、测试该堆排序

  public static void main(String[] args) {
	    Integer[] list = {22, -43, 2, 25, 6, 55, -22, 93, 14,102};
	    HeapSort heapSort = new HeapSort();
	    heapSort.sort(list);
	    
	    for (int i = 0; i < list.length; i++)
	      System.out.print(list[i] + " ");
	  }
测试结果——
bubuko.com,布布扣

三、时间复杂度分析

假设我们现在有n个元素,用h来表示n个元素的高度,由于堆是完全二叉树,于是第一层有1个结点,第二层有两个...h层至少有1个,最多有2^(h - 1)个结点。

1+2+…+2^(h -2) < n <= 1+2+…+ 2^(h-2 )+ 2^(h -1)

化简即得: 2^(h-1) - 1 < n <= 2^h - 1

h - 1 < log(n+1) <= h所以 log(n+1) < h<= log(n+1) +1

所以堆的高度是 O (logn)

因为removeadd方法最多情况下需要从根结点追踪到叶子结点,最多耗费的时间不过是h步,n个元素就调用n次这两个方法,因此堆排序的时间复杂度是 O(NlogN)






算法笔记之堆排序,布布扣,bubuko.com

算法笔记之堆排序

标签:style   blog   http   color   java   使用   strong   io   

原文地址:http://blog.csdn.net/linfeng24/article/details/38323353

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!