登陆 注册

java算法实现之堆排序

守望者 2020-04-26 362人围观 ,发现0个评论 java

     所谓堆排序就是利用堆进行排序的一种算法,这种算法总体来说在选择排序的范畴,但优于直接选择排序java算法实现之选择排序)。

之所以说它属于选择排序是因为它同选择排序一样,在升序排序时,都是先找到最小值,再找次小值,再找此次小值........

之所以说它优于直接选择排序是因为他在具体进行排序过程中使用了堆这种数据结构而不是简单的逐一进行比较来排序。

本篇以增序排列为例。


首先搞清楚什么是堆?

    堆是一种数据结构,属于二叉树范畴,可以看做一个特殊的完全二叉树对象,其特殊性体现为:

堆中的根节点必须大于等于其两个子树/子节点(此时称之为大顶堆),

或者根节点小于等于其两个子树/子节点(此时称之为小顶堆)。


   在一组序列中如{k1,k2,k3.....kn}, 一个堆的根节点和他的左右两个子节点(子树)分别表示为:ki, k2i, k2i+1.

大顶堆中三者关系表示为:ki>=k2i&&ki>=k2i+1;

顶堆中三者关系表示为:ki<=k2i&&ki<=k2i+1;


堆排序算法思路:

     首先,我们需要从逻辑上将java数组转化为二叉树结构,具体操作是:

     将java数组按照从上到下,从左到右的放下平铺元素,

     第一行1个元素,第二行2个元素,第三行4个元素,第四行8个元素.........

    其次,从宏观上看堆排序仍属于选择排序范畴,但是具体排序的实现有所不同,主要体现在利用堆数据结构进行排序。


实现堆数据结构的核心步骤:

  1.  如果根节点大于等于其两个子节点(即ki>=k2i&&ki>=k2i+1),则无需再动;

   2. 否则将最大的子节点与根节点互换位置完成对堆数据结构的塑造,

      与此同时,由于子节点发生了位置调用,则以子节点为根节点的堆的数据结构可能被破坏,

      因为需要重复1,2步骤完成对它的堆结构的重构。

          


堆排序实现过程:

   1. 首先,对数组进行转化,初始化一个大顶堆(如果做降序排序则做一个小顶堆,后面请灵活进行替换),

       其中,要从最后一个非叶子节点开始调整堆(即二叉树结构的倒数第二行的最右边那个元素,索引表现为:arr.lenth/2-1),

       本着自右向左,自下向上的方向调整堆直至完成整个大顶堆结构(整个数个数组)的塑造。


 for (int i = array.length / 2 - 1; i >= 0; i--) {  
	            adjustHeap(array, i, array.length);  //调整堆
	        }


    2.  将完成重构的大顶堆的根元素(索引为0)与相应的数组的尾元素(索引为j)进行位置互换。

   3.   由于大顶堆根元素被换掉,对结构被破坏,因为需要对除索引为j(及其后面)的元素的剩余元素进行重构大顶堆。

   4.   重复2,3步骤直至完成整个数组的增序排序。


       最终排序结果如下:


QQ截图20200426184222.png

   

   以下为相关的函数代码:

	//堆排序函数
  public static int[] heapSort(int[] array) {
	        //从二叉树的最后一个非叶子结点开始,即索引为:array.length/2 - 1
	  
	        for (int i = array.length / 2 - 1; i >= 0; i--) {  
	            adjustHeap(array, i, array.length);  //调整堆
	        }
	  
	        //下面将循环执行位置互换与堆排序,总体而言是自右而左,自下而上排序
	        for (int j = array.length - 1; j > 0; j--) {
	           //将大顶堆的根元素(即堆的最大值)与相应尾元素进行位置互换
	           //  互换完成后索引j即为已完成排序的元素
	            swap(array, 0, j);
	           //对除去已排序完成的元素的堆再次进行堆排序
	            adjustHeap(array, 0, j);
	        }
	        return array;
	    }
	  
	    /**
	    * 整个堆排序最关键的地方
	    * @param array 待组堆
	    * @param i 起始结点
	    * @param length 堆的长度
	    */
	    public static void adjustHeap(int[] array, int i, int length) {
	      
	        int k = 2 * i + 1;        //此时k是根节点i的左子树
	        if(k+1>=length) {return;}
	           
	           //如果左子树大于右子树,则将k指向右子树
	          // 最终k将指向最大子树
	            if (array[k] < array[k + 1]) {   k++;}
	            //如果发现最大子树大于根结点,则进行值的交换
	            if (array[k] > array[i]) {
	                swap(array, i, k);
	                // 此时子节点更换了,那么,以子节点为根的子树会受到影响,所以,循环对子节点所在的树继续进行判断
	                 adjustHeap(array,k,length);  //递归调用adjustHeap方法
	          } 
	            
	    }
	  
	    /**
	    * 交换元素
	    * @param arr
	    * @param a 元素的下标
	    * @param b 元素的下标
	    */
	    public static void swap(int[] arr, int a, int b) {
	        int temp = arr[a];
	        arr[a] = arr[b];
	        arr[b] = temp;
	    }


     转发请附上本文链接:https://tufeng.xyz/java/54.html,谢谢合作!

请发表您的评论
请关注微信公众号
微信二维码