排序算法之堆排序

堆排序基于完全二叉树

若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。

将一维数组看做一颗完全二叉树 如数组[9,15,7,3,25,13,8,21],可以观察出以下规律

arr[i]为arr[2*i+1],arr[2*i+2]的父节点

image.png

堆的定义

最大堆父节点大于等于子节点,体现在数组中就是 arr[i] >= arr[2*i+1]&&arr[i] >= arr[2*i+2]

最小堆 父节点小于等于子节点,体现在数组中就是arr[i <= arr[2*i+1]&&arr[i] <= arr[2*i+2]

上例对应的最大堆为

image.png

堆排序是如何实现的?

假设为正序排列,将数组看做完全二叉树

  1. 构建为最大堆,此时首节点arr[0]就是最大的值
  2. 依次将首节点与无序最大堆的中最后一个节点交换,然后调整堆使其满足最大堆要求

这样就构建了一个从尾节点到首节点依次减小的堆,正好满足数组的正序排列要求

如何构建最大堆?

  1. 从最后一个非叶节点(arr[n/2-1]])开始,提升其子节点(可能为非叶节点)中的最大值到当前节点,并将当前节点值下放的可能的最底层.
  2. 对前面的非叶节点重复 1

这样就构造出了最大堆

在首节点交换后,如何调整堆?

当首节点交换后,最后一个节点已经有序,可以忽略了.此时堆中只有首节点是不符合最大堆的定义的.对首节点应用构建最大堆的步骤即可,注意要忽略掉已经有序的尾节点

算法实现

/**
 * 将一维数组看做一颗二叉树 父节点和子节点的关系为 Nk~=N2k+1,N2k+2
 * 1. 构建堆
 * 2. 调整堆
 */
public class SimpleHeapSort {
    public static void main(String[] args) {
        int[] arr = new int[]{6545,1,4456, 5, 4, 65, 63,5445};
        heapSort(arr);
        for (int n : arr) {
            System.out.println(n);
        }
    }

    private static void heapSort(int[] arr) {
        //从最后一个节点开始扫描,构建最大堆
        for (int i = arr.length / 2 - 1; i >= 0; i--) {
            adjustHeap(arr, i, arr.length);
        }
        //依次将堆顶最大值移到当前的末尾,构建有序数组
        for (int j = arr.length - 1; j > 0; j--) {
            swap(arr, 0, j);
            adjustHeap(arr, 0, j);
        }
    }

    private static void adjustHeap(int[] arr, int i, int length) {
        int temp = arr[i];
        int k = 2 * i + 1;
        int j = i;
        while (k < length) {
            if (k+1<length&&arr[k] < arr[k + 1]) {
                k = k + 1;
            }
            if (temp < arr[k]) {
                arr[j] = arr[k];
                j = k;
            }else{
                break;
            }
            k = k * 2 + 1;
        }
        arr[j] = temp;

    }

    private static void swap(int[] arrays, int i, int j) {
        int temp = arrays[i];
        arrays[i] = arrays[j];
        arrays[j] = temp;
    }
}

重建堆的时间复杂度为O(NlogN)

树的高度为k=lgN+1,从根到叶的筛选,元素比较次数至多2(k-1)次,交换记录至多k次,排序过程中的筛选次数不超过下式:

2(lg(N-1)+lg(N-2)+lg(N-3)+lg(2))<2n()lgN

建堆的时间复杂度为O(N):


链接:

https://www.nowcoder.com/questionTerminal/385157a6b64540c3b233bd12f8a83ee7

来源:牛客网如果从底部最后的父节点开始建堆,那么我们可以大概算一下: 假如有N个节点,那么高度为H=logN,最后一层每个父节点最多只需要下调1次,倒数第二层最多只需要下调2次,顶点最多需要下调H次,而最后一层父节点共有2(H-1)个,倒数第二层公有2(H-2),顶点只有1(2^0)个,所以总共的时间复杂度为s = 1 * 2^(H-1) + 2 * 2^(H-2) + ... + (H-1) * 2^1 + H * 2^0 将H代入后s= 2N - 2 - log2(N),近似的时间复杂度就是O(N)。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 介绍 堆排序是指利用堆这种数据结构所设计的一种排序算法,堆是一个近似完全二叉树的数据结构,并同时满足堆积的性质:即...
    盗梦者_56f2阅读 260评论 0 4
  • 堆排序是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构(通常堆是通过一维数组来实现的),并...
    BEYOND黄阅读 207评论 0 2
  • 时间复杂度:O(Nlog(N))额外空间复杂度:O(1)是否可实现稳定性:否 思路: 把数组逻辑上看成一个二叉树,...
    一凡呀阅读 667评论 0 0
  • 这几天家中有事,前天用了一张复活卡,同时收到了一信提示,其实是用不着提示的,我还没想过停更呢,前几天还在想是不是可...
    弱水三千zzy阅读 316评论 3 3
  • Research Apps 研究型应用使iOS用户利用iOS设备的便利参与研究学习。预先设计好的屏幕和过渡动画可以...
    逃亡的光子阅读 268评论 0 0