zoukankan      html  css  js  c++  java
  • 堆排序

    http://www.cnblogs.com/skywang12345/p/3602162.html

     


    堆是一种数据结构,其约束(根节点大于左右子节点——大根堆,根节点小于左右子节点——小根堆),一般用完全二叉树表示,用数组直接存储

     

    堆排序包括两部分:1,构造堆,保证堆的性质    2,输出根节点,并调整堆(将根节点与叶子节点调换,堆的性质被打破)使得余下的节点使其仍然构成堆

     

     


    堆的插入删除

     
     
    堆的插入:每次插入都是将新数据放在数组最后。可以发现从这个新数据的父结点到根结点必然为一个有序的数列,现在的任务是将这个新数据插入到这个有序数据中——这就类似于直接插入排序中将一个数据并入到有序区间中
     
    //  新加入i结点  其父结点为(i - 1) / 2
    void MinHeapFixup(int a[], int i)
    {
        int j, temp;
     
    temp = a[i];
    j = (i - 1) / 2;      //父结点
    while (j >= 0 && i != 0)
    {
    if (a[j] <= temp)
    break;
     
    a[i] = a[j];     //把较大的子结点往下移动,替换它的子结点
    i = j;
    j = (i - 1) / 2;
    }
    a[i] = temp;
    }
     
    void MinHeapFixup(int a[], int i)
    {
    for (int j = (i - 1) / 2; (j >= 0 && i != 0)&& a[i] > a[j]; i = j, j = (i - 1) / 2)
    Swap(a[i], a[j]);
    }
     
     
    堆的删除:堆中每次都只能删除第0个数据。为了便于重建堆,实际的操作是将最后一个数据的值赋给根结点,然后再从根结点开始进行一次从上向下的调整。调整时先在左右儿子结点中找最小的,如果父结点比这个最小的子结点还小说明不需要调整了,反之将父结点和它交换后再考虑后面的结点。相当于从根结点将一个数据的“下沉”过程。
     
    //  从i节点开始调整,n为节点总数 从0开始计算 i节点的子节点为 2*i+1, 2*i+2
    void MinHeapFixdown(int a[], int i, int n)
    {
        int j, temp;
     
    temp = a[i];
    j = 2 * i + 1;
    while (j < n)
    {
    if (j + 1 < n && a[j + 1] < a[j]) //在左右孩子中找最小的
    j++;
     
    if (a[j] >= temp)
    break;
     
    a[i] = a[j];     //把较小的子结点往上移动,替换它的父结点
    i = j;
    j = 2 * i + 1;
    }
    a[i] = temp;
    }
    //在最小堆中删除数
    void MinHeapDeleteNumber(int a[], int n)
    {
    Swap(a[0], a[n - 1]);
    MinHeapFixdown(a, 0, n - 1);
    }
     

     
    堆排序演示

     

    下面演示heap_sort_asc(a, n)对a={20,30,90,40,70,110,60,10,100,50,80}, n=11进行堆排序过程。下面是数组a对应的初始化结构:

     

     

    1 初始化堆

    在堆排序算法中,首先要将待排序的数组转化成二叉堆。
    下面演示将数组{20,30,90,40,70,110,60,10,100,50,80}转换为最大堆{110,100,90,40,80,20,60,10,30,50,70}的步骤。

     

    1.1 i=11/2-1,即i=4

    上面是maxheap_down(a, 4, 9)调整过程。maxheap_down(a, 4, 9)的作用是将a[4...9]进行下调;a[4]的左孩子是a[9],右孩子是a[10]。调整时,选择左右孩子中较大的一个(即a[10])和a[4]交换。

     

    1.2 i=3

    上面是maxheap_down(a, 3, 9)调整过程。maxheap_down(a, 3, 9)的作用是将a[3...9]进行下调;a[3]的左孩子是a[7],右孩子是a[8]。调整时,选择左右孩子中较大的一个(即a[8])和a[4]交换。

     

    1.3 i=2


    上面是maxheap_down(a, 2, 9)调整过程。maxheap_down(a, 2, 9)的作用是将a[2...9]进行下调;a[2]的左孩子是a[5],右孩子是a[6]。调整时,选择左右孩子中较大的一个(即a[5])和a[2]交换。

     

    1.4 i=1


    上面是maxheap_down(a, 1, 9)调整过程。maxheap_down(a, 1, 9)的作用是将a[1...9]进行下调;a[1]的左孩子是a[3],右孩子是a[4]。调整时,选择左右孩子中较大的一个(即a[3])和a[1]交换。交换之后,a[3]为30,它比它的右孩子a[8]要大,接着,再将它们交换。

     

    1.5 i=0


    上面是maxheap_down(a, 0, 9)调整过程。maxheap_down(a, 0, 9)的作用是将a[0...9]进行下调;a[0]的左孩子是a[1],右孩子是a[2]。调整时,选择左右孩子中较大的一个(即a[2])和a[0]交换。交换之后,a[2]为20,它比它的左右孩子要大,选择较大的孩子(即左孩子)和a[2]交换。

    调整完毕,就得到了最大堆。此时,数组{20,30,90,40,70,110,60,10,100,50,80}也就变成了{110,100,90,40,80,20,60,10,30,50,70}。

     

     

    第2部分 交换数据

    在将数组转换成最大堆之后,接着要进行交换数据,从而使数组成为一个真正的有序数组。
    交换数据部分相对比较简单,下面仅仅给出将最大值放在数组末尾的示意图。

    上面是当n=10时,交换数据的示意图。
    当n=10时,首先交换a[0]和a[10],使得a[10]是a[0...10]之间的最大值;然后,调整a[0...9]使它称为最大堆。交换之后:a[10]是有序的!
    当n=9时, 首先交换a[0]和a[9],使得a[9]是a[0...9]之间的最大值;然后,调整a[0...8]使它称为最大堆。交换之后:a[9...10]是有序的!
    ...
    依此类推,直到a[0...10]是有序的。

     

     


    堆排序时间复杂度


    堆排序的时间复杂度是O(N*lgN)。
    假设被排序的数列中有N个数。遍历一趟的时间复杂度是O(N),需要遍历多少次呢?
    堆排序是采用的二叉堆进行排序的,二叉堆就是一棵二叉树,它需要遍历的次数就是二叉树的深度,而根据完全二叉树的定义,它的深度至少是lg(N+1)。最多是多少呢?由于二叉堆是完全二叉树,因此,它的深度最多也不会超过lg(2N)。因此,遍历一趟的时间复杂度是O(N),而遍历次数介于lg(N+1)和lg(2N)之间;因此得出它的时间复杂度是O(N*lgN)。

     


    堆排序稳定性


    堆排序是不稳定的算法,它不满足稳定算法的定义。它在交换数据的时候,是比较父结点和子节点之间的数据,所以,即便是存在两个数值相等的兄弟节点,它们的相对顺序在排序也可能发生变化。
    算法稳定性 -- 假设在数列中存在a[i]=a[j],若在排序之前,a[i]在a[j]前面;并且排序之后,a[i]仍然在a[j]前面。则这个排序算法是稳定的!

     

     


    堆排序代码实现
    /**
     * 堆排序:C++
     *
     * @author skywang
     * @date 2014/03/11
     */
    #include <iostream>
    using namespace std;
    /* 
     * (最大)堆的向下调整算法
     *
     * 注:数组实现的堆中,第N个节点的左孩子的索引值是(2N+1),右孩子的索引是(2N+2)。
     *     其中,N为数组下标索引值,如数组中第1个数对应的N为0。
     *
     * 参数说明:
     *     a -- 待排序的数组
     *     start -- 被下调节点的起始位置(一般为0,表示从第1个开始)
     *     end   -- 截至范围(一般为数组中最后一个元素的索引)
     */
    void maxHeapDown(int* a, int start, int end)
    {
        int c = start;            // 当前(current)节点的位置
        int l = 2*c + 1;        // 左(left)孩子的位置
        int tmp = a[c];            // 当前(current)节点的大小
        for (; l <= end; c=l,l=2*l+1)
        {
            // "l"是左孩子,"l+1"是右孩子
            if ( l < end && a[l] < a[l+1])
                l++;        // 左右两孩子中选择较大者,即m_heap[l+1]
            if (tmp >= a[l])
                break;        // 调整结束
            else            // 交换值
            {
                a[c] = a[l];
                a[l]= tmp;
            }
        }
    }
    /*
     * 堆排序(从小到大)
     *
     * 参数说明:
     *     a -- 待排序的数组
     *     n -- 数组的长度
     */
    void heapSortAsc(int* a, int n)
    {
        int i,tmp;
        // 从(n/2-1) --> 0逐次遍历。遍历之后,得到的数组实际上是一个(最大)二叉堆。
        for (i = n / 2 - 1; i >= 0; i--)
            maxHeapDown(a, i, n-1);
        // 从最后一个元素开始对序列进行调整,不断的缩小调整的范围直到第一个元素
        for (i = n - 1; i > 0; i--)
        {
            // 交换a[0]和a[i]。交换后,a[i]是a[0...i]中最大的。
            tmp = a[0];
            a[0] = a[i];
            a[i] = tmp;
            // 调整a[0...i-1],使得a[0...i-1]仍然是一个最大堆。
            // 即,保证a[i-1]是a[0...i-1]中的最大值。
            maxHeapDown(a, 0, i-1);
        }
    }
    /* 
     * (最小)堆的向下调整算法
     *
     * 注:数组实现的堆中,第N个节点的左孩子的索引值是(2N+1),右孩子的索引是(2N+2)。
     *     其中,N为数组下标索引值,如数组中第1个数对应的N为0。
     *
     * 参数说明:
     *     a -- 待排序的数组
     *     start -- 被下调节点的起始位置(一般为0,表示从第1个开始)
     *     end   -- 截至范围(一般为数组中最后一个元素的索引)
     */
    void minHeapDown(int* a, int start, int end)
    {
        int c = start;            // 当前(current)节点的位置
        int l = 2*c + 1;        // 左(left)孩子的位置
        int tmp = a[c];            // 当前(current)节点的大小
        for (; l <= end; c=l,l=2*l+1)
        {
            // "l"是左孩子,"l+1"是右孩子
            if ( l < end && a[l] > a[l+1])
                l++;        // 左右两孩子中选择较小者
            if (tmp <= a[l])
                break;        // 调整结束
            else            // 交换值
            {
                a[c] = a[l];
                a[l]= tmp;
            }
        }
    }
    /*
     * 堆排序(从大到小)
     *
     * 参数说明:
     *     a -- 待排序的数组
     *     n -- 数组的长度
     */
    void heapSortDesc(int* a, int n)
    {
        int i,tmp;
        // 从(n/2-1) --> 0逐次遍历每。遍历之后,得到的数组实际上是一个最小堆。
        for (i = n / 2 - 1; i >= 0; i--)
            minHeapDown(a, i, n-1);
        // 从最后一个元素开始对序列进行调整,不断的缩小调整的范围直到第一个元素
        for (i = n - 1; i > 0; i--)
        {
            // 交换a[0]和a[i]。交换后,a[i]是a[0...i]中最小的。
            tmp = a[0];
            a[0] = a[i];
            a[i] = tmp;
            // 调整a[0...i-1],使得a[0...i-1]仍然是一个最小堆。
            // 即,保证a[i-1]是a[0...i-1]中的最小值。
            minHeapDown(a, 0, i-1);
        }
    }
    int main()
    {
        int i;
        int a[] = {20,30,90,40,70,110,60,10,100,50,80};
        int ilen = (sizeof(a)) / (sizeof(a[0]));
        cout << "before sort:";
        for (i=0; i<ilen; i++)
            cout << a[i] << " ";
        cout << endl;
        heapSortAsc(a, ilen);            // 升序排列
        //heapSortDesc(a, ilen);        // 降序排列
        cout << "after  sort:";
        for (i=0; i<ilen; i++)
            cout << a[i] << " ";
        cout << endl;
        return 0;
    }

     





  • 相关阅读:
    解决:Could not resolve archetype org.apache.maven.archetypes
    Spring MVC配置MyBatis输出SQL
    Spring集成MyBatis 通用Mapper以及 pagehelper分页插件
    关于SpringMVC或Struts2接受参数接收不到的原因
    配置quartz启动时就执行一次
    ajaxFileUpload进行文件上传时,总是进入error
    spring mvc注入配置文件里的属性
    java中将一个文件夹下所有的文件压缩成一个文件
    flume failed to start agent because dependencies were not found in classpath
    ubuntu不能安装pip unable to install pip in unbuntu
  • 原文地址:https://www.cnblogs.com/sprint1989/p/3745688.html
Copyright © 2011-2022 走看看