排序篇(5)--堆排序

21 篇文章 0 订阅
16 篇文章 0 订阅

首先明确堆的性质:
堆是具有下列性质的完全二叉树,每个节点的值都大于或等于其左右孩子节点的值,称为大顶堆;或者每个节点的值都小于或等于其左右孩子节点的值,称为小顶堆。

一、堆排序算法

基本思想:以大顶堆为例,将待排序的序列构造成一个大顶堆。此时,整个序列的最大值就是堆顶的根节点。将它移走(其实就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值),然后将剩余的n-1个序列重新构造成一个堆,这样就能得到n个元素中的次大值。如此反复执行,便能得到一个有序序列了。

主要是解决两个问题:
(1)如何由一个无序序列构建成一个堆?
(2)如果在输出堆顶元素后,调整剩余元素成为一个新的堆?

二、堆排序算法实现

package Sort;

import java.util.Arrays;

/**
 * Created by L_kanglin on 2017/3/3.
 */
public class TestHeapSort {
        public static void main(String[] args) {
            int[] a={49,38,65,97,76,13,27,49,78,34,12,64};
            int arrayLength=a.length;
            //将每一次的建推都打印出来
            for(int i=0;i<arrayLength-1;i++){
                //建堆
                buildMaxHeap(a,arrayLength-1-i);
                //交换堆顶和最后一个元素
                swap(a,0,arrayLength-1-i);
                System.out.println(Arrays.toString(a));
            }
        }
        //对data数组从0到lastIndex建大顶堆
        public static void buildMaxHeap(int[] data, int lastIndex){
            //从lastIndex处节点(最后一个节点)的父节点开始
            for(int i=(lastIndex-1)/2;i>=0;i--){
                //k保存正在判断的节点
                int k=i;
                //如果当前k节点的子节点存在
                while(k*2+1<=lastIndex){
                    //k节点的左子节点的索引
                    int biggerIndex=2*k+1;
                    //如果biggerIndex小于lastIndex,即biggerIndex+1代表的k节点的右子节点存在
                    if(biggerIndex<lastIndex){
                        //若果右子节点的值较大
                        if(data[biggerIndex]<data[biggerIndex+1]){
                            //biggerIndex总是记录较大子节点的索引
                            biggerIndex++;
                        }
                    }
                    //如果k节点的值小于其较大的子节点的值
                    if(data[k]<data[biggerIndex]){
                        //交换他们
                        swap(data,k,biggerIndex);
                        //将biggerIndex赋予k,开始while循环的下一次循环,重新保证k节点的值大于其左右子节点的值
                        k=biggerIndex;
                    }else{
                        break;
                    }
                }
            }
        }
        //交换
        private static void swap(int[] data, int i, int j) {
            int tmp=data[i];
            data[i]=data[j];
            data[j]=tmp;
        }
}

运行结果如下:

[13, 78, 65, 49, 76, 64, 27, 49, 38, 34, 12, 97]
[12, 76, 65, 49, 34, 64, 27, 49, 38, 13, 78, 97]
[13, 49, 65, 49, 34, 64, 27, 12, 38, 76, 78, 97]
[38, 49, 64, 49, 34, 13, 27, 12, 65, 76, 78, 97]
[12, 49, 38, 49, 34, 13, 27, 64, 65, 76, 78, 97]
[27, 49, 38, 12, 34, 13, 49, 64, 65, 76, 78, 97]
[13, 34, 38, 12, 27, 49, 49, 64, 65, 76, 78, 97]
[27, 34, 13, 12, 38, 49, 49, 64, 65, 76, 78, 97]
[12, 27, 13, 34, 38, 49, 49, 64, 65, 76, 78, 97]
[13, 12, 27, 34, 38, 49, 49, 64, 65, 76, 78, 97]
[12, 13, 27, 34, 38, 49, 49, 64, 65, 76, 78, 97]

三、堆排序复杂度分析

堆排序的运行时间主要是消耗在初始构建和在重建堆时的反复筛选上。
堆排序的时间复杂度为O(nlogn),由于堆排序堆原始记录的排序状态并不敏感,因此它无论是最好、最坏和平均时间复杂度均为O(nlogn),这在性能上显然优于之前所介绍的排序算法。由于记录的比较与交换是跳跃式进行,因此堆排序也是一种不稳定的排序算法。
同时,由于初始构建堆所需的比较次数较多,因此,它并不适合排序序列个数较少的情况。

文章只是作为自己的学习笔记,借鉴了网上的许多案例,如果觉得阔以的话,希望多交流,在此谢过…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值