zoukankan      html  css  js  c++  java
  • 算法笔记之堆排序

    一、对堆排序的相关了解

    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] + " ");
    	  }
    測试结果——

    三、时间复杂度分析

    如果我们如今有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)






  • 相关阅读:
    linux ss 网络状态工具
    如何安装最新版本的memcached
    如何通过XShell传输文件
    mysql主从复制原理
    聊聊IO多路复用之select、poll、epoll详解
    聊聊 Linux 中的五种 IO 模型
    pytorch中使用cuda扩展
    pytorch中调用C进行扩展
    双线性插值
    python中的装饰器
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/5061107.html
Copyright © 2011-2022 走看看