堆排序

234 阅读4分钟

堆排序的时间复杂度是O(N*longN),空间复杂度O(1),是一个不稳定的排序。

1. 堆结构

可以分为大顶堆和小顶堆,是一个完全二叉树。而堆排序是根据堆的这种数据结构设计的一种排序。

1.1. 大顶堆和小顶堆

大顶堆定义:每个节点的值都大于其左孩子和右孩子节点的值。

小顶堆定义:每个节点的值都小于其左孩子和右孩子节点的值。

原始数据,20,10,59,13,54,70

大顶堆建造过程(图中绿色表示节点需要调整):

第一步:将第一个元素放入一个堆顶,自己和自己比较。

第二步:将10放入第一个元素的左节点上,发现父节点大于新增的子节点,不调整堆结构。

第三步:将59放入第一个元素的右节点上,发现父节点小于新增的子节点,调整堆结构。

第四步:将13放入10节点的左节点上,发现父节点小于新增的子节点,调整堆结构。

第五步:将54放入13节点的右节点上,发现父节点小于新增的子节点,调整堆结构。

第六步:将70放入20节点的左节点上,发现父节点小于新增的子节点,调整堆结构。

调整过后发现上层几点也不满足大顶堆性质,所以继续调整。

关于节点位置在二叉树中的计算公式:

父节点索引 (current - 1)/ 2

左孩子索引 2*current + 1

右孩子索引 2*current + 2

总结:

添加规则:

当最下一层节点未满时,新的节点从左到右依次添加。

当最下一层节点满时,会新增一层,新的节点从左到右依次添加。

只有这样添加才能保证是完全二叉树。

调整规则:

如果发现新加入的节点大于或小于(大顶堆大于,小顶堆小于)父节点,那么调整当前节点与父节点的位置(这里不需要关心另一个节点,因为他一定是最小或最大(大顶堆最小,小顶堆最大)的。

调整后发现调整过后的父节点大于爷爷节点,那么继续调整,直到调整到根。

大顶堆需要调整的伪代码:arr[i] > arr[(current - 1)/ 2]

小顶堆需要调整的伪代码:arr[i] < arr[(current - 1)/ 2]

2. 堆排序

思想:

  1. 将一个数组构建成为大顶堆,此时最大值就在数组中的第一个位置。
  2. 将数组中的头元素与尾元素进行交换,这是需要排序的数组长度-1。
  3. 将需要排序的数组再次构建称为大顶堆,将数组中的头元素与尾元素进行交换,排序的数组长度-1。再次待排序数组进行构建大顶堆,反复执行,直到带排序数组长度为1停止。

画图说明下图如何变成有序的。

第一步:将最大元素与左后一个元素进行交换(绿色表示需要交换,蓝色表示不在使用元素),

第二步:将除70以外的节点再次构建称为大顶堆。

将头节点和尾节点交换(绿色表示需要交换,蓝色表示不在使用元素)。

第N步骤:将剩下的元素构建称为大顶堆,将最到的元素放在尾部,直到戴排序数组长度为1。

3. 代码实现堆排序

package com

class DumpSortDemo {

    static void main(String[] args) {
        def arr = new int[]{20, 10, 59, 13, 54, 70}
        heapSort(arr)
        println arr
    }

    static void heapSort(int[] arr) {
        for (i in 0..<arr.length) {
            buildBigTopDump(arr, arr.length - i - 1)

            swap(arr, 0, arr.length - i - 1)
        }
    }


    static void buildBigTopDump(int[] arr, int endIndex) {
        for (i in 0..endIndex) {

            // 获取父元素位置
            int parentIndex = (i - 1) / 2

            // 新增元素大于父元素 。如果相同则退出
            while (arr[i] > arr[parentIndex]) {

                // 进行交换
                swap(arr, i, parentIndex)

                // 已经到头元素了直接结束
                if (parentIndex == 0) {
                    break
                }


                // 获取爷爷元素位置,让其和新加入的元素进行比较
                i = parentIndex
                parentIndex = (parentIndex - 1) / 2
            }
        }
    }

    static void swap(int[] arr, int i, int j) {
        def temp = arr[i]
        arr[i] = arr[j]
        arr[j] = temp
    }
}

实验数据:20,10,59,13,54,70

运行结果: