堆排序

1、堆排序定义

    n个关键字序列Kl,K2,…,Kn称为堆,当且仅当该序列满足如下性质(简称为堆性质):
    (1) ki≤K2i且ki≤K2i+1

    (2)Ki≥K2i且ki≥K2i+1(1≤i≤ )
    若将此序列所存储的向量R[1..n]看做是一棵完全二叉树的存储结构,则堆实质上是满足如下性质的完全二叉树:树中任一非叶结点的关键字均不大于(或不小于)其左右孩子(若存在)结点的关键字。

    大顶堆和小顶堆

    根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最小者的堆称为小根堆。
    根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最大者,称为大根堆。
注意:
    ①堆中任一子树亦是堆。
    ②以上讨论的堆实际上是二叉堆(Binary Heap),类似地可定义k叉堆。

2、堆排序动画演示

3、堆排序算法的基本思想

(1)用大顶堆排序的基本思想
① 先将初始文件R[1..n]建成一个大顶堆,此堆为初始的无序区 。
② 再将关键字最大的记录R[1](即堆顶)和无序区的最后一个记录R[n]交换,由此得到新的无序区R[1..n-1]和有序区R[n],且满足R[1..n-1].keys≤R[n].key 。
③ 由于交换后新的根R[1]可能违反堆性质,故应将当前无序区R[1..n-1]调整为堆。然后再次将R[1..n-1]中关键字最大的记录R[1]和该区间的最后一个记录R[n-1]交换,由此得到新的无序区R[1..n-2]和有序区R[n-1..n],且仍满足关系R[1..n-2].keys≤R[n-1..n].keys,同样要将R[1..n-2]调整为堆。
    ……
直到无序区只有一个元素为止。
(2)大顶堆排序算法的基本操作
建初始堆:将R[1..n]构造为初始堆;
堆调整:将当前无序区的堆顶记录R[1]和该区间的最后一个记录交换,然后将新的无序区调整为堆。
注意:
①只需做n-1趟排序,选出较大的n-1个关键字即可以使得文件递增有序。
②用小顶堆排序与利用大顶堆类似,只不过其排序结果是递减有序的。堆排序和直接选择排序相反:在任何时刻,堆排序中无序区总是在有序区之前,且有序区是在原向量的尾部由后往前逐步扩大至整个向量为止。

4、实现代码  

 

//最大堆排序

public class MaxHeapProblem {



    public static void buildHeap(int a[]) {

        int heapSize = a.length;

        int filter = (int) Math.floor(heapSize / 2);

        // i从第一个非叶子结点开始

        for (int i = filter - 1; i >= 0; i--) {

            heapAdjust(a, i, heapSize);

        }

    }



    // 已知H.r[i...heapSize]中记录的关键字除H.r[i]外,均满足最大堆结构

    public static void heapAdjust(int arr[], int i, int heapSize) {

        // 当前待调整的元素

        int tmp = arr[i];

        // 该元素的左孩子

        int index = 2 * i + 1;

        while (index < heapSize) {

            // 如果右孩子大于左孩子,则index+1,即交换右孩子和双亲节点

            if (index + 1 < heapSize && arr[index] < arr[index + 1]) {

                index = index + 1;

            }

            if (arr[i] < arr[index]) {

                // 交换孩子和双亲节点

                arr[i] = arr[index];

                

                // 重新赋初值

                i = index;

                index = 2 * i + 1;

            } 

            // 已经是最大堆

            else {

                break;

            }

            // 把双亲值赋给孩子节点

            arr[i] = tmp;

        }

    }

    

    private void heapAdjust2(int[] arr, int i, int heapSize) {

        int maxIndex = i;

        if (2 * i + 1 <= heapSize - 1 && arr[2 * i + 1] > arr[i])

            maxIndex = 2 * i + 1;

        if (2 * i + 2 <= heapSize - 1 && arr[i * 2 + 2] > arr[maxIndex])

            maxIndex = 2 * i + 2;

        if (maxIndex != i) {

            int temp = arr[maxIndex];

            arr[maxIndex] = arr[i];

            arr[i] = temp;

            heapAdjust2(arr, maxIndex, heapSize);

        }

        

    }



    public static void heapSort(int a[]) {

        int heapSize = a.length;

        for (int i = heapSize - 1; i > 0; i--) {

            // 交换堆顶和最后一个元素

            int tmp = a[0];

            a[0] = a[i];

            a[i] = tmp;

            // 在heapSize范围内根结点的左右子树都已经是最大堆,所以只需看新交换的堆顶元素是否满足最大堆结构即可。

            // 将H.r[0...i]重新调整为最大堆

            heapAdjust(a, 0, i);

        }

    }



    public static void main(String[] args) {

        int arr[] = new int[] { 6, 5, 3, 1, 8, 7, 2, 4 };

        buildHeap(arr);

        System.out.println("初始建立的最大堆是:");

        for (int data : arr)

            System.out.print(data + " ");

        System.out.println();

        System.out.println("堆经过筛选调整后,排序结果为:");

        heapSort(arr);

        for (int data : arr)

            System.out.print(data + " ");

    }



}

 5、复杂度分析

   堆排序的平均时间复杂度和最坏时间复杂度均为:O(nlgn)

  参考资料:

http://www.cnblogs.com/rollenholt/archive/2012/04/15/2450175.html

http://blog.csdn.net/morewindows/article/details/6709644

你可能感兴趣的:(堆排序)