zoukankan      html  css  js  c++  java
  • 堆排序的学习

    几句废话先:


    本人最近在学习研究数据结构和算法,之所以为什么学习它,第一:是我看到到了现在学习的瓶颈,一直停留在使用别人的什么什么框架或者算法之类的东西,没有对程序更深层次的学习探究。第二:想在IT行业走得更加的长远,数据结构是必须要迈的一道坎。

    另外本人从上一篇博客开始已经步入javascript,不在或者很少探讨.NET平台相关内容。

    主题:


    arr.heap-size

    至于什么事堆,我在这里也不再的啰嗦,阅读本博文,已经默认你认识它,如果您是大牛,请忽视本博文或者找我的错,我很欢迎  :)

    在堆排序之前我们要先知道一个非常基础的一个东西就是arr.heap-size。我刚开始看的时候,对这个不怎么重视,觉得,它不就是等于arr.length吗?并且在学习之初的建堆上,也没有多大的用处,但是在后面的排序中,优化性能方面它发挥了很大的作用。它的定义说白了就是在一个数组arr中有效的元素个数,换句话说:arr[1,arr.length]中可能有数据,但是在arr[1,arr.heap-size]中存放的是有效的数据。

    最大堆

    在有些书上,对于最大堆的定义有些啰嗦(个人觉得),其实说白了就是除了根节点外,任何一个孩子的根节点相对其孩子来说是最大的。至于最小堆就是它的反向。最小堆是用于构造优先队列的,这里暂时不说,毕竟现在都23点了,明儿还要早起,扯远了。。在以后的博文中会陆续讲解。最大堆的性质也就是它的定义所说的,维护最大堆的性质的时间复杂度是大Ο(logN) ,这里的N不是方!!

    我们先来看看怎么样去维护一个堆的性质:

    下面用JS吧,毕竟现在日常就是用JS,啥语言都一样:

    var arr = [4,1,3,2,16,9,10,14,8,7],
        heapLen = arr.length,
        halfLen = heapLen / 2,
        laster;
    
    function buidHeap(i){
    	var left = 2 * i + 1,
    		right = 2 * i + 2;
    	if(left < heapLen && arr[left] > arr[i]){
    		laster = left;
    	}else{
    		laster = i;
    	}
    
    	if(right < heapLen && arr[right] > arr[laster]){
    		laster = right;
    	}
    
    	if(laster !== i){
    		var temp;
    		temp = arr[i];
    		arr[i] = arr[laster];
    		arr[laster] = temp;
    		buidHeap(laster);
    	}
    }
    

     维护最大堆的一个最主要的思想就是始终坚持它的性质,我们在arr[i]、arr[left]和arr[right]中选出最大的,并且将它的下标保存在laster中,但是在交换了之后考虑到还要继续维护它的性质,我们可以进行递归的调用。维护一个最大堆的代价也就是两个:第一:arr[i]、arr[left]和arr[right]的调换是常数时间,任意一个孩子的子树是≤2/3 * n的

    所以它的时间T(n) = T(2/3 *N);

    在上面我们只是在维护最大堆的性质,那么我们怎么去建堆那?

    var arr = [4,1,3,2,16,9,10,14,8,7],
        heapLen = arr.length,
        halfLen = heapLen / 2,
        laster;
    
    function buidHeap(i){
    	var left = 2 * i + 1,
    		right = 2 * i + 2;
    	if(left < heapLen && arr[left] > arr[i]){
    		laster = left;
    	}else{
    		laster = i;
    	}
    
    	if(right < heapLen && arr[right] > arr[laster]){
    		laster = right;
    	}
    
    	if(laster !== i){
    		var temp;
    		temp = arr[i];
    		arr[i] = arr[laster];
    		arr[laster] = temp;
    		buidHeap(laster);
    	}
    }
    
    
    function exe(cb){
    	for(var i = heapLen;i >= 0;i--){
    		buidHeap(i);
    	}
    	cb();
    }
    

      最下面的exe()函数就是一个建堆的过程。

    到这我们的堆就已经建好了,我们的堆排序怎么实现?

    实现堆排序:


    这里有两个办法,第一个将已经刷选出的元素放在另外一个数组中,因为在最大堆中,根节点最大,所以我们每移除一次根节点都要在进行一次最大堆性质的维护。但是复制到新的一个数组这样就消耗了两倍的空间,这样做虽然可以实现最终的目的,但是还是不怎么好。第二个办法就是在本博文开始不是说到heap-size了吗,在这里就用上了,它表示的是有效元素,既然是有效的,我们每一次刷选后,把最大元素放在最后,同时heap-size - 1;这样不就节省了空间了吗?

    代码:

    var arr = [4,1,3,2,16,9,10,14,8,7],
        heapLen = arr.length,
        halfLen = heapLen / 2,
        laster;
    
    function buidHeap(i){
    	var left = 2 * i + 1,
    		right = 2 * i + 2;
    	if(left < heapLen && arr[left] > arr[i]){
    		laster = left;
    	}else{
    		laster = i;
    	}
    
    	if(right < heapLen && arr[right] > arr[laster]){
    		laster = right;
    	}
    
    	if(laster !== i){
    		var temp;
    		temp = arr[i];
    		arr[i] = arr[laster];
    		arr[laster] = temp;
    		buidHeap(laster);
    	}
    }
    
    
    function exe(cb){
    	for(var i = heapLen;i >= 0;i--){
    		buidHeap(i);
    	}
    	cb();
    }
    
    function buidMaxHeap(cb){
    	exe(function(){
    		console.log('------最大堆已建好------');
    		for(var i = arr.length - 1;i>=1;i--){
    			var temp;
    			temp = arr[0];
    			arr[0] = arr[i];
    			arr[i] = temp;
    			heapLen -= 1;
    			buidHeap(0);
    		}
    	});
    	cb();
    };
    
    (function heapsort(){
    	buidMaxHeap(function(){
    		console.log('------堆排序已派好------');
    		for(var i = 0;i<arr.length;i++){
    			console.log(arr[i]);
    		}
    	});
    })();
    

    最后想说:


    本博文只是对堆排序的浅意义上的说明,没有更深次的探讨它的时间复杂度,第一个原因就是一些数学符号弄不出来,第二个就是:我个人语言表现力不足,你可以Google之。

    本博文属于原创,允许转载,但请标明原始连接来源!!

  • 相关阅读:
    【动态规划】01背包问题
    【Spring源码解读】bean标签中的属性(一)你可能还不够了解的 scope 属性
    【LeetCode】无重复字符串最长子串
    【LeetCode】两数之和
    LeetCode单排日记
    你不可不知的Java引用类型【总结篇】
    你不可不知的Java引用类型之——PhantomReference源码详解
    你不可不知的Java引用类型之——WeakReference源码详解
    你不可不知的Java引用类型之——SoftReference源码详解
    你不可不知的Java引用类型之——ReferenceQueue源码详解
  • 原文地址:https://www.cnblogs.com/struCoder/p/3830645.html
Copyright © 2011-2022 走看看