java 大顶堆

堆排序的时间复杂度,最好,最差,平均都是O(nlogn),空间复杂度O(1),是不稳定的排序

  1. 堆(或二叉堆),类似于完全二叉树,除叶子节点外,每个节点均拥有左子树和右子树,同时左子树和右子树也是堆。

    • 小顶堆:父节点的值 <= 左右孩子节点的值
    • 大顶堆:父节点的值 >= 左右孩子节点的值
  2. 堆的存储: 
    用一个数组存储堆就可以了,如【19, 17, 20, 18, 16, 21】

这里写图片描述

对于数组中的第 i 个节点(从0开始),有如下规律:

  • 如果父节点存在,则它的父节点是 (i - 1) / 2; 比如3的父亲是(3-1)/2=1
  • 如果左孩子存在,则它的左孩子是 2 * i + 1; 比如1的左孩子是2*1+1=3
  • 如果右孩子存在,则右孩子是 2 * i + 2;比如1的右孩子是2*1+2=4

假设当前要排序的数组是:arr = [19, 17, 20, 18, 16, 21],下面以大顶堆为例,介绍如何构建大顶堆

1、由于叶子节点没有左孩子和右孩子,所以不必从叶子节点开始调整堆,即不从18、16、21开始调整,直接从20开始调整,直至堆顶。 用伪代码(Java)描述如下:

for(int i = arr.length / 2 - 1; i >= 0; i--) {
  // 调整();
  fix_down(arr, i, arr.length-1);
}
 
 
  • 1
  • 2
  • 3
  • 4

2、如果当前节点 小于 左右孩子的最大值【此处假设是右孩子】,则交换当前节点的值与右孩子的值,并从右孩子开始,继续向下调整。由于20不大于21,因此交换20和21。

接下来从17开始,因为17 < 18, 所以交换17与18的值。

接下来,由于19 < 21 ,所以交换19和21,又由于19 < 20,所以需要继续交换19和20。

这里写图片描述

3、 经过上一步,就得到了一个大顶堆,接下来就是堆排序的过程

在这个大顶堆中,堆顶元素是整个堆中的最大值,取出堆顶元素,并与堆中的最后一个元素交换位置,即21与19交换,然后调整堆(21不动)。不断重复这个过程,每一次取出堆顶元素,并与最后一个元素交换位置,直至最后一个元素就是堆顶,这样就可以得到一个自上而下的递增的堆。

public static void head_sort(int[] arr) {
    // 取出堆顶元素,与最后一个元素交换,调整堆
    for(int i = arr.length - 1; i >= 0; i--) {
        int temp = arr[i]; // 最后一个元素
        arr[i] = arr[0];
        arr[0] = temp;
        fix_down(arr, 0, i-1); // 注意此处是i-1; 刚刚交换后的最后一个元素不参与调整,即21不参与调整
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

这里写图片描述

堆排序完整代码如下:

/**
 * 
 * @author lijialin
 * @since 2017/08/28 
 */
public class HeapSort {

    public static void print(int[] arr) {
        for(int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }

    public static void create_heap(int[] arr) {
        for(int i = arr.length / 2 - 1; i >= 0; i--) {
            fix_down(arr, i, arr.length-1);
        }
    }

    public static void fix_down(int[] arr, int i, int end) {
        int child = (i << 1) + 1; // 当前节点的左孩子
        int temp = arr[i];

        while(child <= end) {
            // 选出两个孩子较大的那个
            if(child < end && arr[child+1] > arr[child]) {
                child++;
            }
            if(temp < arr[child]) {
                arr[i] = arr[child]; // 孩子节点与当前节点替换
                i = child; 
                child = (i << 1) + 1;
            }else {
                break;
            }
        }
        arr[i] = temp;
    }


    public static void head_sort(int[] arr) {
        // 取出堆顶元素,与最后一个元素交换,调整堆
        for(int i = arr.length - 1; i >= 0; i--) {
            int temp = arr[i]; // 最后一个元素
            arr[i] = arr[0];
            arr[0] = temp;
            fix_down(arr, 0, i-1);

        }
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        // Test case 1
        int[] arr = {19,17,20,18,16,21};
        create_heap(arr); // 创建堆
        head_sort(arr);
        print(arr); // 16 17 18 19 20 21

        // Test case 2
        int[] arr1 = {16,7,3,20,17,8};
        create_heap(arr1); 
        head_sort(arr1);
        print(arr1); // 3 7 8 16 17 20  

        // Test case 3
        int[] arr2 = {5,4,3,2,1};
        create_heap(arr2); 
        head_sort(arr2);
        print(arr2); // 1 2 3 4 5 

        // Test case 4
        int[] arr3 = {1,1,1,1};
        create_heap(arr3); 
        head_sort(arr3);
        print(arr3); // 1 1 1 1 

    }

}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82

总结:

  • 堆排序是不稳定的排序

    • 在待排序的文件中,若存在多个关键字相同的记录,经过排序后,这些记录之间的相对次序保持不变,该排序方法是稳定的;反之,若相对次序变化,则排序不稳定。
  • 堆排序的时间复杂度,最好最差平均都是O(nlogn),空间复杂度是O(n)。父节点与子节点交换时,需要使用一个中间变量,所以是O(1),当然创建堆的空间不计算在内。

所有内容来自转载
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值